From 6a052277c37dff298b1ca18c4f07c0facf1beecf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 28 May 2026 17:49:46 -0700 Subject: [PATCH 01/49] std.Build: document a handful of functions --- lib/std/Build.zig | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index a3bf69604e4ec7e68847e6caced7ed91fbbcf269..08fa2ed36583d58f32d01b64df4e05c9ab10ae00 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -290,6 +290,7 @@ const UserValue = union(enum) { lazy_path_list: std.array_list.Managed(LazyPath), }; +/// Build system implementation detail. pub fn create( graph: *Graph, root: Cache.Path, @@ -681,9 +682,9 @@ fn hashUserInputOptionsMap(allocator: Allocator, user_input_options: UserInputOp /// Create a set of key-value pairs that can be converted into a Zig source /// file and then inserted into a Zig compilation's module table for importing. -/// In other words, this provides a way to expose build.zig values to Zig -/// source code with `@import`. -/// Related: `Module.addOptions`. +/// +/// This provides a way to expose build.zig values to Zig source code with +/// `@import`. Related: `Module.addOptions`. pub fn addOptions(b: *Build) *Step.Options { return Step.Options.create(b); } @@ -970,6 +971,7 @@ pub fn addConfigHeader( return config_header_step; } +/// Deprecated, call `Graph.dupeString` instead. pub fn dupe(b: *Build, bytes: []const u8) []const u8 { return b.graph.dupeString(bytes); } @@ -1292,6 +1294,8 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw } } +/// Creates a top-level build step, exposed to the CLI user and advertised in +/// the "--help" menu. pub fn step(b: *Build, name: []const u8, description: []const u8) *Step { const graph = b.graph; const arena = graph.arena; @@ -1476,6 +1480,7 @@ pub fn standardTargetOptionsQueryOnly(b: *Build, args: StandardTargetOptionsArgs return args.default_target; } +/// Build system implementation detail. pub fn addUserInputOption(b: *Build, name_raw: []const u8, value_raw: []const u8) error{OutOfMemory}!bool { const graph = b.graph; const arena = graph.arena; @@ -1532,6 +1537,7 @@ pub fn addUserInputOption(b: *Build, name_raw: []const u8, value_raw: []const u8 return false; } +/// Build system implementation detail. pub fn addUserInputFlag(b: *Build, name_raw: []const u8) error{OutOfMemory}!bool { const graph = b.graph; const name = graph.dupeString(name_raw); @@ -1592,6 +1598,7 @@ fn markInvalidUserInput(b: *Build) void { b.invalid_user_input = true; } +/// Build system implementation detail. pub fn validateUserInputDidItFail(b: *Build) bool { // Make sure all args are used. var it = b.user_input_options.iterator(); @@ -2178,6 +2185,7 @@ pub inline fn lazyImport( comptime unreachable; // Bad @dependencies source } +/// Build system implementation detail. pub fn dependencyFromBuildZig( b: *Build, /// The build.zig struct of the dependency, normally obtained by `@import` of the dependency. @@ -2334,6 +2342,7 @@ fn dependencyInner( return dep; } +/// Build system implementation detail. pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void { switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).@"fn".return_type.?)) { .void => build_zig.build(b), @@ -2710,7 +2719,10 @@ pub fn systemIntegrationOption( test { _ = Cache; + _ = Configuration; + _ = Module; _ = Step; _ = Configuration; _ = &findProgram; + _ = abi; } -- 2.54.0 From c616c00db613c2a6fe7d5acfdfb884c006baa600 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 29 May 2026 20:17:37 -0700 Subject: [PATCH 02/49] Maker: restore "new" as the default build summary in watch mode --- lib/compiler/Maker.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index c31ba826d00a25807284c75e2209388d5e17b4d7..4d4ceceea255b4c94681843e9e7f4c23c823c4e8 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -592,7 +592,7 @@ pub fn main(init: process.Init.Minimal) !void { .error_style = error_style, .multiline_errors = multiline_errors, - .summary = summary orelse if (watch or webui_listen != null) .line else .failures, + .summary = summary orelse if (watch or webui_listen != null) .new else .failures, }; defer { maker.memory_blocked_steps.deinit(gpa); @@ -997,7 +997,7 @@ fn makeStepNames( t.setColor(.reset) catch {}; } - w.writeAll("\n") catch {}; + w.writeByte('\n') catch {}; if (maker.summary == .line) break :summary; -- 2.54.0 From a04f90c35b116ede4d81c8f03f08d0f79a908dea Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 1 Jun 2026 17:38:33 -0700 Subject: [PATCH 03/49] introduce std.Build API for making configure script depend on fs --- build.zig | 5 +- lib/compiler/configurer.zig | 33 ++++++++- lib/std/Build.zig | 121 +++++++++++++++++++++++++++++--- lib/std/Build/Cache.zig | 28 ++++---- lib/std/Build/Configuration.zig | 75 +++++++++++++------- src/main.zig | 64 +++++++++-------- test/src/Cases.zig | 27 ++++--- test/tests.zig | 20 +++--- 8 files changed, 275 insertions(+), 98 deletions(-) diff --git a/build.zig b/build.zig index ebf163bf5c136a61ecad9120c75c4215cdd8345e..e97fac39188412a77f57f3559247cf07a40c25ed 100644 --- a/build.zig +++ b/build.zig @@ -264,9 +264,8 @@ pub fn build(b: *std.Build) !void { std.process.exit(1); } - // Ensure git version changes get picked up - // https://codeberg.org/ziglang/zig/issues/35473 - b.graph.poisonCache(); + // Ensure git version changes get picked up. + b.dependOnFileContents(b.graph.path(.build_root, ".git/HEAD")); const version_string = b.fmt("{d}.{d}.{d}", .{ zig_version.major, zig_version.minor, zig_version.patch }); diff --git a/lib/compiler/configurer.zig b/lib/compiler/configurer.zig index f044dc311945d20b330d472553ad5dded0d3138f..809bb9e6263c3a9c0e128bb6a677589fef6054c9 100644 --- a/lib/compiler/configurer.zig +++ b/lib/compiler/configurer.zig @@ -133,7 +133,7 @@ pub fn main(init: process.Init.Minimal) !void { .off => .no_color, }; - try builder.runBuild(root); + builder.runBuild(root); if (builder.validateUserInputDidItFail()) { fatal(" access the help menu with 'zig build -h'", .{}); @@ -632,6 +632,37 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { var s: Serialize = .{ .wc = wc, .arena = arena }; + try wc.path_deps.ensureTotalCapacityPrecise(gpa, graph.configure_dependencies.items.len); + for ( + graph.configure_dependencies.items, + wc.path_deps.addManyAsSliceAssumeCapacity(graph.configure_dependencies.items.len), + ) |src, *dest| { + dest.* = .{ + .flags = .{ + .base = switch (src.lazy_path) { + .src_path, .dependency => .build_root, + .generated => unreachable, + .cwd_relative => .cwd, + .relative => |r| r.base, + }, + .mode = src.mode, + }, + .sub = switch (src.lazy_path) { + .src_path => |sp| try wc.addString(sp.sub_path), + .generated => unreachable, + .cwd_relative => |sub_path| try wc.addString(sub_path), + .dependency => |d| try wc.addString(d.sub_path), + .relative => |r| try wc.addString(r.sub_path), + }, + .pkg = switch (src.lazy_path) { + .src_path => |sp| .init(try s.builderToPackage(sp.owner)), + .generated => unreachable, + .cwd_relative, .relative => .none, + .dependency => |d| .init(try s.builderToPackage(d.dependency.builder)), + }, + }; + } + // 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(); diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 08fa2ed36583d58f32d01b64df4e05c9ab10ae00..84dd3f84169b776c3e5197ee02979af30d7eb143 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -64,6 +64,11 @@ pkg_hash: []const u8, /// A mapping from dependency names to package hashes. available_deps: AvailableDeps, +pub const ConfigureDependency = struct { + lazy_path: LazyPath, + mode: std.Build.Configuration.PathDep.Mode, +}; + pub const ReleaseMode = enum { off, any, @@ -102,6 +107,12 @@ pub const Graph = struct { /// Observing this data causes cache poisoning. See `CachePoison`. search_prefixes: std.ArrayList([]const u8) = .empty, + /// Populated by calling one of: + /// * `dependOnFileContents` + /// * `dependOnFileMetadata` + /// * `dependOnDirectory` + configure_dependencies: ArrayList(ConfigureDependency) = .empty, + /// If the cache is poisoned means that the **configure logic** had side /// effects, or otherwise did something that could not be tracked by the /// cache system. @@ -165,7 +176,7 @@ pub const Graph = struct { /// A path whose components and contents are known at some point during /// `Step` resolution, relative to the provided base directory. - pub fn path(graph: *Graph, base: Configuration.Path.Base, sub_path: []const u8) LazyPath { + pub fn path(graph: *Graph, base: Configuration.LazyPath.Relative.Base, sub_path: []const u8) LazyPath { return .{ .relative = .{ .base = base, .sub_path = @This().dupePath(graph, sub_path), @@ -204,6 +215,9 @@ pub const Graph = struct { /// did something that could not be tracked by the cache system. /// /// See `CachePoison` documentation for more details. + /// + /// As an alternative to calling this function, consider these APIs instead: + /// * `dependOnFileContents` pub fn poisonCache(graph: *Graph) void { switch (graph.cache_poison) { .pure => graph.cache_poison = .poisoned, @@ -2318,14 +2332,13 @@ fn dependencyInner( .root_dir = .{ .path = build_root_string, .handle = Io.Dir.cwd().openDir(io, build_root_string, .{}) catch |err| - process.fatal("unable to open {s}: {t}", .{ build_root_string, err }), + process.fatal("failed to open {q}: {t}", .{ build_root_string, err }), }, }; - const sub_builder = b.createChild(name, dep_root, pkg_hash, pkg_deps, user_input_options) catch - @panic("unhandled error"); + const sub_builder = b.createChild(name, dep_root, pkg_hash, pkg_deps, user_input_options) catch @panic("OOM"); if (build_zig) |bz| { - sub_builder.runBuild(bz) catch @panic("unhandled error"); + sub_builder.runBuild(bz); if (sub_builder.validateUserInputDidItFail()) { std.debug.dumpCurrentStackTrace(.{ .first_address = @returnAddress() }); @@ -2343,11 +2356,10 @@ fn dependencyInner( } /// Build system implementation detail. -pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void { +pub fn runBuild(b: *Build, build_zig: anytype) void { switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).@"fn".return_type.?)) { - .void => build_zig.build(b), - .error_union => try build_zig.build(b), - else => @compileError("expected return type of build to be 'void' or '!void'"), + .error_union => return build_zig.build(b) catch unreachable, + else => return build_zig.build(b), } } @@ -2411,7 +2423,7 @@ pub const LazyPath = union(enum) { }, relative: struct { - base: Configuration.Path.Base, + base: Configuration.LazyPath.Relative.Base, sub_path: []const u8 = "", pub fn eql(a: @This(), b: @This()) bool { @@ -2717,6 +2729,95 @@ pub fn systemIntegrationOption( } } +/// Indicates that the build.zig logic depends on a particular file's contents. +/// +/// If the file is created, deleted, or has its contents changed, the configure +/// phase will be repeated. If the inode or mtime change, but the file contents +/// remain the same, it will not cause the configure logic to be repeated. +/// +/// This is an alternative to `Graph.poisonCache` that avoids making every invocation +/// of `zig build` into a cache miss. +/// +/// Only a subset of `LazyPath` are supported: +/// - Relative to cwd +/// - Relative to any package root +/// - Relative to zig cache or zig installation +/// +/// If the file would be inside one of the search prefixes, then the dependency +/// cannot be tracked; `Graph.poisonCache` must be used instead. +pub fn dependOnFileContents(b: *Build, lazy_path: LazyPath) void { + validateConfigureDependency(lazy_path); + const graph = b.graph; + graph.configure_dependencies.append(graph.arena, .{ + .lazy_path = lazy_path.dupe(graph), + .mode = .contents, + }) catch @panic("OOM"); +} + +/// Indicates that the build.zig logic depends on a particular file's size, +/// inode, mtime, and contents. +/// +/// If the file is created, deleted, has its contents changed, or the inode +/// changes, or the mtime changes, the configure phase will be repeated. +/// +/// This is an alternative to `Graph.poisonCache` that avoids making every invocation +/// of `zig build` into a cache miss. +/// +/// Only a subset of `LazyPath` are supported: +/// - Relative to cwd +/// - Relative to any package root +/// - Relative to zig cache or zig installation +/// +/// If the file would be inside one of the search prefixes, then the dependency +/// cannot be tracked; `Graph.poisonCache` must be used instead. +pub fn dependOnFileMetadata(b: *Build, lazy_path: LazyPath) void { + validateConfigureDependency(lazy_path); + const graph = b.graph; + graph.configure_dependencies.append(graph.arena, .{ + .lazy_path = lazy_path.dupe(graph), + .mode = .metadata, + }) catch @panic("OOM"); +} + +/// Indicates that the build.zig logic depends on a particular directory's entries. +/// +/// This is an alternative to `Graph.poisonCache` that avoids making every invocation +/// of `zig build` into a cache miss. +/// +/// If any file is created, deleted, or renamed in this directory, the +/// configure phase will be repeated. +/// +/// Only a subset of `LazyPath` are supported: +/// - Relative to cwd +/// - Relative to any package root +/// - Relative to zig cache or zig installation +/// +/// If the directory would be inside one of the search prefixes, then the dependency +/// cannot be tracked; `Graph.poisonCache` must be used instead. +pub fn dependOnDirectory(b: *Build, lazy_path: LazyPath) void { + validateConfigureDependency(lazy_path); + const graph = b.graph; + graph.configure_dependencies.append(graph.arena, .{ + .lazy_path = lazy_path.dupe(graph), + .mode = .directory, + }) catch @panic("OOM"); +} + +fn validateConfigureDependency(lazy_path: LazyPath) void { + switch (lazy_path) { + .src_path, .cwd_relative, .dependency => {}, // OK + .generated => @panic("configure phase cannot depend on files generated during make phase"), + .relative => |relative| switch (relative.base) { + .cwd, .build_root, .local_cache, .global_cache, .zig_exe, .zig_lib => {}, // OK + .install_prefix, + .install_lib, + .install_bin, + .install_include, + => @panic("configure phase cannot depend on files installed during make phase"), + }, + } +} + test { _ = Cache; _ = Configuration; diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 33c3148dd22080b5916100f4c3e210dabe38e14d..0880d2abeaa9e4fa9580673b3a16f028c2b3d0f4 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -57,7 +57,7 @@ pub fn prefixes(cache: *const Cache) []const Directory { return cache.prefixes_buffer[0..cache.prefixes_len]; } -const PrefixedPath = struct { +pub const PrefixedPath = struct { prefix: u8, sub_path: []const u8, @@ -1000,18 +1000,21 @@ pub const Manifest = struct { /// other files will need to be recompiled if the imported file is changed. pub fn addFilePost(self: *Manifest, file_path: []const u8) !void { assert(self.manifest_file != null); - const gpa = self.cache.gpa; const prefixed_path = try self.cache.findPrefix(file_path); - errdefer gpa.free(prefixed_path.sub_path); + var keep = false; + defer if (!keep) gpa.free(prefixed_path.sub_path); + keep = try addPrefixedPathPost(self, prefixed_path); + } - const gop = try self.files.getOrPutAdapted(gpa, prefixed_path, FilesAdapter{}); - errdefer _ = self.files.pop(); + pub fn addPrefixedPathPost(man: *Manifest, prefixed_path: PrefixedPath) !bool { + assert(man.manifest_file != null); + const gpa = man.cache.gpa; - if (gop.found_existing) { - gpa.free(prefixed_path.sub_path); - return; - } + const gop = try man.files.getOrPutAdapted(gpa, prefixed_path, FilesAdapter{}); + errdefer _ = man.files.pop(); + + if (gop.found_existing) return false; gop.key_ptr.* = .{ .prefixed_path = prefixed_path, @@ -1022,10 +1025,11 @@ pub const Manifest = struct { .contents = null, }; - self.files.lockPointers(); - defer self.files.unlockPointers(); + man.files.lockPointers(); + defer man.files.unlockPointers(); - try self.populateFileHash(gop.key_ptr); + try man.populateFileHash(gop.key_ptr); + return true; } pub fn addPathPost(man: *Manifest, path: Path) !void { diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig index 12746e4498f76b745898559660dfb03c4722c233..8e36cad974a76f24ef6e12f08802995c2b748544 100644 --- a/lib/std/Build/Configuration.zig +++ b/lib/std/Build/Configuration.zig @@ -10,8 +10,7 @@ const native_endian = builtin.target.cpu.arch.endian(); string_bytes: []u8, steps: []Step, -path_deps_base: []Path.Base, -path_deps_sub: []String, +path_deps: []PathDep, unlazy_deps: []String, system_integrations: []SystemIntegration, available_options: []AvailableOption, @@ -57,7 +56,7 @@ pub const Wip = struct { system_integrations: std.ArrayList(SystemIntegration) = .empty, available_options: std.ArrayList(AvailableOption) = .empty, steps: std.ArrayList(Step) = .empty, - path_deps: std.MultiArrayList(Path) = .empty, + path_deps: std.ArrayList(PathDep) = .empty, search_prefixes: std.ArrayList(String) = .empty, extra: std.ArrayList(u32) = .empty, next_generated_file_index: u32 = 0, @@ -154,7 +153,7 @@ pub const Wip = struct { const header: Header = .{ .string_bytes_len = @intCast(wip.string_bytes.items.len), .steps_len = @intCast(wip.steps.items.len), - .path_deps_len = @intCast(wip.path_deps.len), + .path_deps_len = @intCast(wip.path_deps.items.len), .unlazy_deps_len = @intCast(wip.unlazy_deps.items.len), .system_integrations_len = @intCast(wip.system_integrations.items.len), .available_options_len = @intCast(wip.available_options.items.len), @@ -171,8 +170,7 @@ pub const Wip = struct { @ptrCast(&header), wip.string_bytes.items, @ptrCast(wip.steps.items), - @ptrCast(wip.path_deps.items(.base)), - @ptrCast(wip.path_deps.items(.sub)), + @ptrCast(wip.path_deps.items), @ptrCast(wip.unlazy_deps.items), @ptrCast(wip.system_integrations.items), @ptrCast(wip.available_options.items), @@ -1551,9 +1549,22 @@ pub const LazyPath = union(@This().Tag) { pub const Flags = packed struct(u32) { tag: Tag = .relative, - base: Path.Base, + base: Base, _: u16 = 0, }; + + pub const Base = enum(u8) { + cwd, + local_cache, + global_cache, + build_root, + zig_exe, + zig_lib, + install_prefix, + install_lib, + install_bin, + install_include, + }; }; }; @@ -1597,6 +1608,26 @@ pub const Package = struct { return package.dep_prefix.slice(c); } }; + + pub const OptionalIndex = enum(u32) { + none = max_u32 - 1, + root = max_u32, + _, + + pub fn init(i: Index) OptionalIndex { + const result: OptionalIndex = @enumFromInt(@intFromEnum(i)); + assert(result != .none); + return result; + } + + pub fn unwrap(this: @This()) ?Index { + return switch (this) { + .none => null, + .root => .root, + _ => @enumFromInt(@intFromEnum(this)), + }; + } + }; }; pub const Module = struct { @@ -1833,24 +1864,20 @@ pub const OptionalStringList = enum(u32) { } }; -pub const Path = extern struct { - base: Base, +pub const PathDep = extern struct { + flags: Flags, sub: String, + pkg: Package.OptionalIndex, - pub const Base = enum(u8) { - cwd, - local_cache, - global_cache, - build_root, - zig_exe, - zig_lib, - install_prefix, - install_lib, - install_bin, - install_include, + pub const Flags = packed struct(u32) { + mode: Mode, + base: LazyPath.Relative.Base, + _: u16 = 0, }; - pub fn toCachePath(path: Path, c: *const Configuration, arena: Allocator) std.Build.Cache.Path { + pub const Mode = enum(u8) { directory, contents, metadata }; + + pub fn toCachePath(path: PathDep, c: *const Configuration, arena: Allocator) std.Build.Cache.Path { _ = c; _ = arena; _ = path; @@ -3430,8 +3457,7 @@ pub fn load(arena: Allocator, reader: *Io.Reader) LoadError!Configuration { const result: Configuration = .{ .string_bytes = try arena.alloc(u8, header.string_bytes_len), .steps = try arena.alloc(Step, header.steps_len), - .path_deps_sub = try arena.alloc(String, header.path_deps_len), - .path_deps_base = try arena.alloc(Path.Base, header.path_deps_len), + .path_deps = try arena.alloc(PathDep, header.path_deps_len), .unlazy_deps = try arena.alloc(String, header.unlazy_deps_len), .system_integrations = try arena.alloc(SystemIntegration, header.system_integrations_len), .available_options = try arena.alloc(AvailableOption, header.available_options_len), @@ -3444,8 +3470,7 @@ pub fn load(arena: Allocator, reader: *Io.Reader) LoadError!Configuration { var vecs = [_][]u8{ result.string_bytes, @ptrCast(result.steps), - @ptrCast(result.path_deps_base), - @ptrCast(result.path_deps_sub), + @ptrCast(result.path_deps), @ptrCast(result.unlazy_deps), @ptrCast(result.system_integrations), @ptrCast(result.available_options), diff --git a/src/main.zig b/src/main.zig index 4dd56dd3b0623c7836ce04604c9066b562a223a4..84b6553ff64750279537a242729f6ecf9c807639 100644 --- a/src/main.zig +++ b/src/main.zig @@ -5454,6 +5454,9 @@ fn cmdBuild( } defer Fork.deinitList(forks.items); + var file_system_inputs: std.ArrayList(u8) = .empty; + defer file_system_inputs.deinit(gpa); + // This loop is re-evaluated when the build script exits with an indication that it // could not continue due to missing lazy dependencies. const configuration_path: Path, const poisoned: bool = cp: while (true) { @@ -5679,6 +5682,7 @@ fn cmdBuild( try root_mod.deps.put(arena, "@build", build_mod); + file_system_inputs.clearRetainingCapacity(); var create_diag: Compilation.CreateDiagnostic = undefined; const comp = Compilation.create(gpa, arena, io, &create_diag, .{ .libc_installation = libc_installation, @@ -5702,6 +5706,7 @@ fn cmdBuild( .reference_trace = reference_trace, .debug_compile_errors = debug_compile_errors, .environ_map = environ_map, + .file_system_inputs = &file_system_inputs, }) catch |err| switch (err) { error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}), else => |e| fatal("failed to create compilation: {t}", .{e}), @@ -5764,10 +5769,10 @@ fn cmdBuild( .argv = configure_argv.items, .stdout = .{ .file = config_tmp_file }, .progress_node = child_node, - }) catch |err| fatal("failed to spawn configure script {s}: {t}", .{ configure_argv.items[0], err }); + }) catch |err| fatal("failed to spawn configure script {q}: {t}", .{ configure_argv.items[0], err }); defer child.kill(io); break :term child.wait(io) catch |err| - fatal("failed to wait configure script {s}: {t}", .{ configure_argv.items[0], err }); + fatal("failed to wait configure script {q}: {t}", .{ configure_argv.items[0], err }); }; if (!term.success()) { // Failure to produce the configuration file. @@ -5816,6 +5821,21 @@ fn cmdBuild( try config_man.addPathPost(conf_path.toCachePath(&configuration, arena)); } + // We need to add to the configuration cache the source files of + // configurer itself, so that the maker process can watch the file system + // for those changes and restart itself. By doing this, we make it + // possible to bypass creating a Compilation for configurer on + // Configuration cache hit. + { + var it = mem.splitScalar(u8, file_system_inputs.items, 0); + while (it.next()) |input| { + _ = try config_man.addPrefixedPathPost(.{ + .prefix = input[0], + .sub_path = input[1..], + }); + } + } + // If it is poisoned, there is no point in moving it to cached // location. Just leave it in the tmp directory. if (configuration.poisoned) { @@ -6247,14 +6267,15 @@ fn jitCmdInner( child_argv.appendSliceAssumeCapacity(args); + if (EnvVar.ZIG_DEBUG_CMD.isSet(environ_map)) { + const cmd = try std.mem.join(arena, " ", child_argv.items); + std.debug.print("{s}\n", .{cmd}); + } + if (process.can_replace and options.capture == null) { - if (EnvVar.ZIG_DEBUG_CMD.isSet(environ_map)) { - const cmd = try std.mem.join(arena, " ", child_argv.items); - std.debug.print("{s}\n", .{cmd}); - } const err = process.replace(io, .{ .argv = child_argv.items, .environ_map = environ_map }); const cmd = try std.mem.join(arena, " ", child_argv.items); - fatal("the following command failed to execve with '{t}':\n{s}", .{ err, cmd }); + fatal("the following command failed to execve with {t}:\n{s}", .{ err, cmd }); } if (!process.can_spawn) { @@ -6264,7 +6285,7 @@ fn jitCmdInner( }); } - switch (t: { + const term = t: { _ = try io.lockStderr(&.{}, .no_color); defer io.unlockStderr(); @@ -6282,28 +6303,13 @@ fn jitCmdInner( } break :t try child.wait(io); - }) { - .exited => |code| { - if (code == 0) { - if (options.capture != null) return; - return cleanExit(io); - } - const cmd = try std.mem.join(arena, " ", child_argv.items); - fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); - }, - .signal => |sig| { - const cmd = try std.mem.join(arena, " ", child_argv.items); - fatal("the following build command terminated with signal {t}:\n{s}", .{ sig, cmd }); - }, - .stopped => |sig| { - const cmd = try std.mem.join(arena, " ", child_argv.items); - fatal("the following build command stopped with signal {t}:\n{s}", .{ sig, cmd }); - }, - .unknown => { - const cmd = try std.mem.join(arena, " ", child_argv.items); - fatal("the following build command crashed:\n{s}", .{cmd}); - }, + }; + if (term.success()) { + if (options.capture != null) return; + return cleanExit(io); } + const cmd = try std.mem.join(arena, " ", child_argv.items); + fatal("the following build command {f}:\n{s}", .{ term, cmd }); } const info_zen = diff --git a/test/src/Cases.zig b/test/src/Cases.zig index f321d15851e117539f78f38681bd20cb8e06b1c4..af5dcfdddd6fd1c7671f88ff812637bfbcdfd794 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -316,20 +316,19 @@ pub fn addCompile( /// Each file should include a test manifest as a contiguous block of comments at /// the end of the file. The first line should be the test type, followed by a set of /// key-value config values, followed by a blank line, then the expected output. -pub fn addFromDir(ctx: *Cases, dir: Io.Dir, b: *std.Build) void { +pub fn addFromDir(ctx: *Cases, dir: Io.Dir, path_from_root: []const u8, b: *std.Build) void { var current_file: []const u8 = "none"; - ctx.addFromDirInner(dir, ¤t_file, b) catch |err| { - std.debug.panicExtra( - @returnAddress(), - "test harness failed to process file '{s}': {s}\n", - .{ current_file, @errorName(err) }, - ); + ctx.addFromDirInner(dir, path_from_root, ¤t_file, b) catch |err| { + std.debug.panicExtra(@returnAddress(), "test harness failed to process file {q}: {t}\n", .{ + current_file, err, + }); }; } fn addFromDirInner( ctx: *Cases, iterable_dir: Io.Dir, + path_from_root: []const u8, /// This is kept up to date with the currently being processed file so /// that if any errors occur the caller knows it happened during this file. current_file: *[]const u8, @@ -340,11 +339,19 @@ fn addFromDirInner( var filenames: ArrayList([]const u8) = .empty; while (try it.next(io)) |entry| { - if (entry.kind != .file) continue; - // Ignore stuff such as .swp files if (!knownFileExtension(entry.basename)) continue; - try filenames.append(ctx.arena, try ctx.arena.dupe(u8, entry.path)); + + switch (entry.kind) { + .file => { + b.dependOnFileContents(b.path(b.pathJoin(&.{ path_from_root, entry.path }))); + try filenames.append(ctx.arena, try ctx.arena.dupe(u8, entry.path)); + }, + .directory => { + b.dependOnDirectory(b.path(b.pathJoin(&.{ path_from_root, entry.path }))); + }, + else => continue, + } } for (filenames.items) |filename| { diff --git a/test/tests.zig b/test/tests.zig index 62d54e981d0bef3093ee02d5be8c0de8e421a52a..78f397d3f4e0c47053d98c5a51049a8dd7507fa9 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -3258,14 +3258,12 @@ pub fn addCases( var cases = @import("src/Cases.zig").init(gpa, arena, io); - // Ensure changes to these files get picked up - // https://codeberg.org/ziglang/zig/issues/35473 - b.graph.poisonCache(); + b.dependOnDirectory(b.path("test/cases")); var dir = try b.root.openDir(io, "test/cases", .{ .iterate = true }); defer dir.close(io); - cases.addFromDir(dir, b); + cases.addFromDir(dir, "test/cases", b); try @import("cases.zig").addCases(&cases, build_options, b); cases.lowerToBuildSteps( @@ -3320,22 +3318,28 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step, test_filters: []cons }), }); - // Ensure changes to these files get picked up - // https://codeberg.org/ziglang/zig/issues/35473 - b.graph.poisonCache(); + b.dependOnDirectory(b.path("test/incremental")); var dir = try b.root.openDir(io, "test/incremental", .{ .iterate = true }); defer dir.close(io); var it = try dir.walk(b.graph.arena); while (try it.next(io)) |entry| { - if (entry.kind != .file) continue; if (std.mem.endsWith(u8, entry.basename, ".swp")) continue; for (test_filters) |test_filter| { if (std.mem.indexOf(u8, entry.path, test_filter)) |_| break; } else if (test_filters.len > 0) continue; + switch (entry.kind) { + .file => {}, + .directory => { + b.dependOnDirectory(b.path(b.pathJoin(&.{ "test", "incremental", entry.path }))); + }, + else => continue, + } + b.dependOnFileContents(b.path(b.pathJoin(&.{ "test", "incremental", entry.path }))); + for (incremental_targets) |target_str| { const run = b.addRunArtifact(incr_check); run.setName(b.fmt("incr-check {s} '{s}'", .{ target_str, entry.basename })); -- 2.54.0 From 38992fc017f11ec39769043ff309c25dba819760 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 3 Jun 2026 16:22:13 -0700 Subject: [PATCH 04/49] env: remove build_command it's a regular jit cmd now --- src/dev.zig | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/dev.zig b/src/dev.zig index bba67696f2f92cf522949f68ffc35370f3daf86d..a6f97799157440329f3b248959e47f1585cfc1b3 100644 --- a/src/dev.zig +++ b/src/dev.zig @@ -76,7 +76,6 @@ pub const Env = enum { .test_command, .run_command, .ar_command, - .build_command, .clang_command, .stdio_listen, .build_import_lib, @@ -162,7 +161,6 @@ pub const Env = enum { else => Env.ast_gen.supports(feature), }, .@"aarch64-linux" => switch (feature) { - .build_command, .stdio_listen, .incremental, .aarch64_backend, @@ -179,7 +177,6 @@ pub const Env = enum { else => Env.sema.supports(feature), }, .@"powerpc-linux" => switch (feature) { - .build_command, .stdio_listen, .incremental, .x86_64_backend, @@ -210,7 +207,6 @@ pub const Env = enum { else => Env.sema.supports(feature), }, .@"x86_64-linux" => switch (feature) { - .build_command, .stdio_listen, .incremental, .legalize, @@ -251,7 +247,6 @@ pub const Feature = enum { test_command, run_command, ar_command, - build_command, clang_command, cc_command, translate_c_command, -- 2.54.0 From 0c978ba957ad1d44f5a4952b2d6dce121a5b05e6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 22 Jun 2026 17:32:23 -0700 Subject: [PATCH 05/49] WIP: migrate build and fetch commands to Maker process --- build.zig | 3 + lib/compiler/Maker.zig | 1419 +++++++++++++- {src/Package => lib/compiler/Maker}/Fetch.zig | 41 +- .../compiler/Maker}/Fetch/git.zig | 0 .../Fetch/git/testdata/testrepo-sha1.idx | Bin .../Fetch/git/testdata/testrepo-sha1.pack | Bin .../Fetch/git/testdata/testrepo-sha256.idx | Bin .../Fetch/git/testdata/testrepo-sha256.pack | Bin {src => lib/compiler/Maker}/Package.zig | 2 - .../compiler/Maker}/Package/Manifest.zig | 0 lib/compiler/configurer.zig | 5 +- lib/std/Build/Cache.zig | 2 +- lib/std/Build/Configuration.zig | 2 +- lib/std/zig.zig | 383 +++- src/Compilation.zig | 158 +- src/{Package => }/Module.zig | 34 +- src/Zcu.zig | 1 - src/Zcu/PerThread.zig | 1 - src/dev.zig | 2 - src/introspect.zig | 220 --- src/main.zig | 1688 +---------------- src/print_env.zig | 3 +- src/print_targets.zig | 3 +- 23 files changed, 1784 insertions(+), 2183 deletions(-) rename {src/Package => lib/compiler/Maker}/Fetch.zig (99%) rename {src/Package => lib/compiler/Maker}/Fetch/git.zig (100%) rename {src/Package => lib/compiler/Maker}/Fetch/git/testdata/testrepo-sha1.idx (100%) rename {src/Package => lib/compiler/Maker}/Fetch/git/testdata/testrepo-sha1.pack (100%) rename {src/Package => lib/compiler/Maker}/Fetch/git/testdata/testrepo-sha256.idx (100%) rename {src/Package => lib/compiler/Maker}/Fetch/git/testdata/testrepo-sha256.pack (100%) rename {src => lib/compiler/Maker}/Package.zig (98%) rename {src => lib/compiler/Maker}/Package/Manifest.zig (100%) rename src/{Package => }/Module.zig (97%) delete mode 100644 src/introspect.zig diff --git a/build.zig b/build.zig index e97fac39188412a77f57f3559247cf07a40c25ed..19bf41dc607d3b93e96bf53598d9945f8bd5bcf4 100644 --- a/build.zig +++ b/build.zig @@ -175,6 +175,9 @@ pub fn build(b: *std.Build) !void { ".tar", // exclude files from lib/std/zip/testdata ".zip", + // exclude files from lib/compiler/Maker/Fetch/git/testdata + ".idx", + ".pack", // others "README.md", }, diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 4d4ceceea255b4c94681843e9e7f4c23c823c4e8..6fe474bee810bc95c0b6e6bcd6bfa063b60e8686 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -17,6 +17,10 @@ const log = std.log; const mem = std.mem; const process = std.process; const Color = std.zig.Color; +const EnvVar = std.zig.EnvVar; +const default_local_zig_cache_basename = std.zig.default_local_zig_cache_basename; +const allocPrint = std.fmt.allocPrint; +const stringToEnum = std.meta.stringToEnum; const Fuzz = @import("Maker/Fuzz.zig"); const Graph = @import("Maker/Graph.zig"); @@ -25,10 +29,11 @@ const Watch = @import("Maker/Watch.zig"); const WebServer = @import("Maker/WebServer.zig"); const ScannedConfig = @import("Maker/ScannedConfig.zig"); const PkgConfig = @import("Maker/PkgConfig.zig"); +const Fetch = @import("Maker/Fetch.zig"); +const Package = @import("Maker/Package.zig"); pub const std_options: std.Options = .{ .side_channels_mitigations = .none, - .http_disable_tls = true, }; gpa: Allocator, @@ -100,6 +105,15 @@ const ErrorStyle = enum { const MultilineErrors = enum { indent, newline, none }; const Summary = enum { all, new, failures, line, none }; +/// Used to build the -M flags to pass to build-exe. +const CliModule = struct { + name: []const u8, + root_path: []const u8, + deps: Deps = .empty, + + const Deps = std.array_hash_map.String(*CliModule); +}; + pub fn main(init: process.Init.Minimal) !void { // The build runner is long-lived in the following use cases: // * `--watch` mode @@ -124,70 +138,54 @@ pub fn main(init: process.Init.Minimal) !void { const arena = arena_instance.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 configure_path = expectArgOrFatal(args, &arg_idx, "--configuration"); + 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 = .{ - .path = zig_lib_dir, - .handle = try cwd.openDir(io, zig_lib_dir, .{}), - }; - - const build_root_directory: Cache.Directory = .{ - .path = build_root, - .handle = try cwd.openDir(io, build_root, .{}), - }; - - const local_cache_directory: Cache.Directory = .{ - .path = local_cache_root, - .handle = try cwd.createDirPathOpen(io, local_cache_root, .{}), + .path = zig_lib_arg, + .handle = try cwd.openDir(io, zig_lib_arg, .{}), }; const global_cache_directory: Cache.Directory = .{ - .path = global_cache_root, - .handle = try cwd.createDirPathOpen(io, global_cache_root, .{}), + .path = global_cache_arg, + .handle = try cwd.createDirPathOpen(io, global_cache_arg, .{}), }; var graph: 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, + .cache = undefined, + .zig_exe = zig_exe_arg, .environ_map = try init.environ.createMap(arena), .global_cache_root = global_cache_directory, - .local_cache_root = local_cache_directory, + .local_cache_root = undefined, .zig_lib_directory = zig_lib_directory, - .build_root_directory = build_root_directory, + .build_root_directory = undefined, + .random_seed = parseRandomSeed(seed_arg), }; - 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 cmd = stringToEnum(enum { fetch, build }, cmd_name) orelse fatal("bad command name: {q}", .{ cmd_name }); + switch (cmd) { + .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 = false; + var print_configuration: enum {none, zon, path} = .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; @@ -201,39 +199,120 @@ pub fn main(init: process.Init.Minimal) !void { 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; - if (std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(&graph.environ_map)) |str| { - if (std.meta.stringToEnum(ErrorStyle, str)) |style| { + 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 (std.zig.EnvVar.ZIG_BUILD_MULTILINE_ERRORS.get(&graph.environ_map)) |str| { - if (std.meta.stringToEnum(MultilineErrors, str)) |style| { + if (EnvVar.ZIG_BUILD_MULTILINE_ERRORS.get(&graph.environ_map)) |str| { + if (stringToEnum(MultilineErrors, str)) |style| { multiline_errors = style; } } - while (nextArg(args, &arg_idx)) |arg| { + try configure_argv.ensureUnusedCapacity(arena, 16); + try cached_passthru_configure.ensureUnusedCapacity(arena, 16); + + _ = configure_argv.addOneAssumeCapacity(); // configurer executable + 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, "-")) { - if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { + 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); + continue; + } 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); // Intentionally "--system" only; not the path. + continue; + } else if (mem.cutPrefix(u8, arg, "--color=")) |rest| { + color = stringToEnum(Color, rest) orelse + fatal("expected --color=[auto|on|off]; found {q}", .{arg}); + + try cached_passthru_configure.append(arena, @intCast(configure_argv.items.len)); + configure_argv.appendAssumeCapacity(arg); + continue; + } else if (mem.eql(u8, arg, "--cache-poison")) { + cache_poison = .poisoned; + configure_argv.appendAssumeCapacity("--cache-poison=poisoned"); + continue; + } else if (mem.cutPrefix(u8, arg, "--cache-poison=")) |rest| { + // Allow the configurer process to report parse failure. + if (stringToEnum(std.Build.Graph.CachePoison, rest)) |poison| { + cache_poison = poison; + } + configure_argv.appendAssumeCapacity(arg); + continue; + } else if (mem.eql(u8, arg, "--verbose")) { + // Intentionally is added both to make and configure but + // does not go into the cache hash. + configure_argv.appendAssumeCapacity(arg); + } else if (mem.eql(u8, arg, "--search-prefix")) { + const prefix = nextArgOrFatal(args, &arg_i); + // This argument is cache poisonous: it does not go into + // the cache and configurer must set the poison bit when + // choosing to observe it. + configure_argv.addManyAsArrayAssumeCapacity(2).* = .{ arg, prefix }; + continue; + } 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.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 = true; + 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_idx); + 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_idx); + override_lib_dir = nextArgOrFatal(args, &arg_i); } else if (mem.eql(u8, arg, "--prefix-exe-dir")) { - override_bin_dir = nextArgOrFatal(args, &arg_idx); + override_bin_dir = nextArgOrFatal(args, &arg_i); } else if (mem.eql(u8, arg, "--prefix-include-dir")) { - override_include_dir = nextArgOrFatal(args, &arg_idx); + override_include_dir = nextArgOrFatal(args, &arg_i); } else if (mem.eql(u8, arg, "--sysroot")) { - graph.sysroot = nextArgOrFatal(args, &arg_idx); + graph.sysroot = nextArgOrFatal(args, &arg_i); } else if (mem.eql(u8, arg, "--maxrss")) { - const max_rss_text = nextArgOrFatal(args, &arg_idx); + 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")) { @@ -253,7 +332,7 @@ pub fn main(init: process.Init.Minimal) !void { .{ "h", std.time.ns_per_hour }, .{ "hour", std.time.ns_per_hour }, }; - const timeout_str = nextArgOrFatal(args, &arg_idx); + 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}, @@ -274,50 +353,46 @@ pub fn main(init: process.Init.Minimal) !void { ); test_timeout_ns = std.math.lossyCast(u64, unit_factor * num_parsed); } else if (mem.eql(u8, arg, "--search-prefix")) { - try graph.search_prefixes.append(arena, nextArgOrFatal(args, &arg_idx)); + try graph.search_prefixes.append(arena, nextArgOrFatal(args, &arg_i)); } else if (mem.eql(u8, arg, "--libc")) { - graph.libc_file = nextArgOrFatal(args, &arg_idx); + graph.libc_file = nextArgOrFatal(args, &arg_i); } else if (mem.eql(u8, arg, "--color")) { - const next_arg = nextArg(args, &arg_idx) orelse + const next_arg = nextArg(args, &arg_i) orelse fatalWithHint("expected [auto|on|off] after {q}", .{arg}); - color = std.meta.stringToEnum(Color, next_arg) orelse { + color = stringToEnum(Color, next_arg) orelse { fatalWithHint("expected [auto|on|off] after {q}, found {q}", .{ arg, next_arg, }); }; } else if (mem.eql(u8, arg, "--error-style")) { - const next_arg = nextArg(args, &arg_idx) orelse + const next_arg = nextArg(args, &arg_i) orelse fatalWithHint("expected style after {q}", .{arg}); - error_style = std.meta.stringToEnum(ErrorStyle, next_arg) orelse { + 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_idx) orelse + const next_arg = nextArg(args, &arg_i) orelse fatalWithHint("expected style after {q}", .{arg}); - multiline_errors = std.meta.stringToEnum(MultilineErrors, next_arg) orelse { + 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_idx) orelse + const next_arg = nextArg(args, &arg_i) orelse fatalWithHint("expected [all|new|failures|line|none] after {q}", .{arg}); - summary = std.meta.stringToEnum(Summary, next_arg) orelse { + summary = stringToEnum(Summary, next_arg) orelse { fatalWithHint("expected [all|new|failures|line|none] after {q}, found {q}", .{ arg, next_arg, }); }; - } else if (mem.eql(u8, arg, "--seed")) { - const next_arg = nextArg(args, &arg_idx) orelse - fatalWithHint("expected u32 after {q}", .{arg}); - graph.random_seed = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err| { - fatal("unable to parse seed {q} as unsigned 32-bit integer: {t}", .{ next_arg, err }); - }; + } 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_idx) orelse + 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}", .{ @@ -333,8 +408,7 @@ pub fn main(init: process.Init.Minimal) !void { fatal("invalid web UI address {q}: {t}", .{ addr_str, err }); }; } else if (mem.eql(u8, arg, "--debug-log")) { - const next_arg = nextArgOrFatal(args, &arg_idx); - try graph.debug_log_scopes.append(arena, next_arg); + 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")) { @@ -344,19 +418,21 @@ pub fn main(init: process.Init.Minimal) !void { } 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 = std.meta.stringToEnum(std.builtin.OptimizeMode, rest) orelse + graph.debug_compiler_runtime_libs = stringToEnum(std.builtin.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")) { // --glibc-runtimes was the old name of the flag; kept for compatibility for now. - graph.libc_runtimes_dir = nextArgOrFatal(args, &arg_idx); + graph.libc_runtimes_dir = nextArgOrFatal(args, &arg_i); } else if (mem.eql(u8, arg, "--verbose")) { graph.verbose = true; } 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")) { @@ -439,7 +515,7 @@ pub fn main(init: process.Init.Minimal) !void { } 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_idx); + 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| { @@ -449,7 +525,7 @@ pub fn main(init: process.Init.Minimal) !void { threaded.setAsyncLimit(.limited(n)); graph.max_jobs = n; } else if (mem.eql(u8, arg, "--")) { - run_args = argsRest(args, arg_idx); + run_args = argsRest(args, arg_i); break; } else { fatalWithHint("unrecognized argument: {s}", .{arg}); @@ -459,8 +535,40 @@ pub fn main(init: process.Init.Minimal) !void { } } - const NO_COLOR = std.zig.EnvVar.NO_COLOR.isSet(&graph.environ_map); - const CLICOLOR_FORCE = std.zig.EnvVar.CLICOLOR_FORCE.isSet(&graph.environ_map); + const cwd_path = std.zig.getResolvedCwd(io, arena) catch |err| + fatal("resolving current directory path failed: {t}", .{err}); + + const build_root = try findBuildRoot(arena, io, .{ + .cwd_path = cwd_path, + .build_file = build_file, + }); + + 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 Dir.path.join(arena, &.{build_root.directory.path orelse ".", 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(graph.build_root_directory); + graph.cache.addPrefix(zig_lib_directory); + graph.cache.addPrefix(graph.local_cache_root); + graph.cache.addPrefix(global_cache_directory); + 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), @@ -468,6 +576,555 @@ pub fn main(init: process.Init.Minimal) !void { .off => .no_color, }; + const main_progress_node = std.Progress.start(io, .{ + .disable_printing = (graph.stderr_mode.? == .no_color), + }); + defer main_progress_node.end(); + + { + // Cache lookup for configure options. If we get a match, we can skip + // execution of the configure script. If not, we get the file path to pass + // to the configure process. + var config_man = graph.cache.obtain(); + defer config_man.deinit(); + + for (cached_passthru_configure.items) |i| + config_man.hash.addBytes(configure_argv.items[i]); + + // Prevents a `zig build` from getting a false positive cache hit following + // a `zig build --cache-poison=ignored`. + config_man.hash.add(cache_poison == .ignored); + + // Normally the build runner is compiled for the host target but here is + // some code to help when debugging edits to the build runner so that you + // can make sure it compiles successfully on other targets. + const resolved_target: Package.Module.ResolvedTarget = t: { + if (debug_target) |triple| { + const target_query = try std.Target.Query.parse(.{ .arch_os_abi = triple }); + config_man.hash.addBytes(triple); + break :t .{ + .result = std.zig.resolveTargetQueryOrFatal(io, target_query), + .is_native_os = false, + .is_native_abi = false, + .is_explicit_dynamic_linker = false, + }; + } + break :t .{ + .result = std.zig.resolveTargetQueryOrFatal(io, .{}), + .is_native_os = true, + .is_native_abi = true, + .is_explicit_dynamic_linker = false, + }; + }; + + 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", + }; + + configure_argv.items[conf_argv_index_build_root] = build_root.directory.path orelse cwd_path; + + var http_client: std.http.Client = .{ .allocator = gpa, .io = io }; + defer http_client.deinit(); + + var unlazy_set: Package.Fetch.JobQueue.UnlazySet = .{}; + var fork_set: Package.Fetch.JobQueue.ForkSet = .{}; + + { + // Populate fork_set. + var group: Io.Group = .init; + defer group.cancel(io); + + for (forks.items) |*fork| + group.async(io, Fork.load, .{ io, gpa, fork, color }); + + try group.await(io); + + for (forks.items) |*fork| { + if (fork.failed) process.exit(1); + try fork_set.put(arena, .{ + .path = fork.path, + .manifest_ast = fork.manifest_ast, + .manifest = fork.manifest, + .uses = 0, + }, {}); + } + } + defer Fork.deinitList(forks.items); + + var file_system_inputs: std.ArrayList(u8) = .empty; + defer file_system_inputs.deinit(gpa); + + var build_configurer_argv: std.ArrayList(u8) = .empty; + defer build_configurer_argv.deinit(gpa); + + var dependencies_source: std.ArrayList(u8) = .empty; + defer dependencies_source.deinit(gpa); + + const configurer_root_src_path: Cache.Path = .{ + .root_dir = graph.zig_lib_directory, + .sub_path = "lib/compiler/configurer.zig", + }; + + const root_build_src_path: Cache.Path = .{ + .root_dir = build_root.directory, + .sub_path = build_root.build_zig_basename, + }; + + try build_configurer_argv.appendSlice(gpa, &.{ + graph.zig_exe, "build-exe", // + "--cache-dir", graph.local_cache_root.path orelse ".", // + "--global-cache-dir", graph.global_cache_root.path orelse ".", // + "--zig-lib-dir", graph.zig_lib_directory.path orelse ".", // + "--name", "configurer", // + "-fsingle-threaded", // + }); + if (graph.libc_file) |libc_file| { + try build_configurer_argv.appendSlice(gpa, &.{ "--libc", libc_file}); + } + if (graph.reference_trace) |n| { + try build_configurer_argv.append(gpa, try allocPrint(arena, "-freference-trace={d}", .{n})); + } + if (graph.debug_compile_errors) { + try build_configurer_argv.append(gpa, "--debug-compile-errors"); + } + try build_configurer_argv.appendSlice(gpa, &.{ + "--dep", "@build", // + "--dep", "@dependencies", // + try allocPrint(arena, "-Mroot={f}", .{configurer_root_src_path}), // + try allocPrint(arena, "-M@build={f}", .{root_build_src_path}), // + }); + + // In the loop below, after doing the fetch operation, the argv will be + // truncated at this point, dependencies added, and then the + // "--listen=-" arg appended at the end. + const argv_deps_index = build_configurer_argv.items.len - 1; + + //const root_mod = try arena.create(CliModule); + //root_mod.* = .{ + // .name = "root", + // .root_path = try configurer_root_src_path.toString(arena), + //}; + + const build_mod = try arena.create(CliModule); + build_mod.* = .{ + .name = "@build", + .root_path = try root_build_src_path.toString(arena), + }; + defer build_mod.deps.deinit(gpa); + + const deps_mod = try arena.create(CliModule); + deps_mod.* = .{ + .name = "@dependencies", + .root_path = undefined, + }; + defer deps_mod.deps.deinit(gpa); + + // This loop is re-evaluated when the build script exits with an indication that it + // could not continue due to missing lazy dependencies. + const configuration_path: Path, const poisoned: bool = cp: while (true) { + //root_mod.deps.clearRetainingCapacity(); + build_mod.deps.clearRetainingCapacity(); + deps_mod.deps.clearRetainingCapacity(); + + // We want to release all the locks before executing the child process, so we make a nice + // big block here to ensure the cleanup gets run when we extract out our argv. + { + + + + { + const fetch_prog_node = main_progress_node.start("Fetch Packages", 0); + defer fetch_prog_node.end(); + + // Reset fork match counts. + for (fork_set.keys()) |*fork| fork.uses = 0; + + var job_queue: Package.Fetch.JobQueue = .{ + .io = io, + .http_client = &http_client, + .global_cache = graph.global_cache_root, + .local_storage = &.{ + .cache_root = .{ .root_dir = graph.local_cache_root }, + .pkg_root = pkg_root, + }, + .recursive = true, + .debug_hash = false, + .unlazy_set = unlazy_set, + .fork_set = fork_set, + .mode = fetch_mode, + .prog_node = fetch_prog_node, + .read_only = system_pkg_dir_path != null, + }; + defer job_queue.deinit(); + + if (system_pkg_dir_path == null) { + try http_client.initDefaultProxies(arena, &graph.environ_map); + } + + try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1); + try job_queue.table.ensureUnusedCapacity(gpa, 1); + + const phantom_package_root: Cache.Path = .{ .root_dir = build_root.directory }; + + var fetch: Package.Fetch = .{ + .arena = std.heap.ArenaAllocator.init(gpa), + .location = .{ .relative_path = phantom_package_root }, + .location_tok = 0, + .hash_tok = .none, + .name_tok = 0, + .lazy_status = .eager, + .remote_package_root = phantom_package_root, + .parent_package_root = phantom_package_root, + .parent_manifest_ast = null, + .prog_node = fetch_prog_node, + .job_queue = &job_queue, + .omit_missing_hash_error = true, + .allow_missing_paths_field = false, + .use_latest_commit = false, + + .package_root = undefined, + .error_bundle = undefined, + .manifest = undefined, + .manifest_ast = undefined, + .have_manifest = false, + .computed_hash = undefined, + .has_build_zig = true, + .oom_flag = false, + .latest_commit = null, + + .cli_module = build_mod, + }; + + job_queue.all_fetches.appendAssumeCapacity(&fetch); + + job_queue.table.putAssumeCapacityNoClobber( + Package.Fetch.relativePathDigest(phantom_package_root, graph.global_cache_root), + &fetch, + ); + + job_queue.group.async(io, Package.Fetch.workerRun, .{ &fetch, "root" }); + try job_queue.group.await(io); + + { + // Ensure that forks were actually used. This is done + // before printing manifest errors because using a fork can + // prevent them. + var any_unused = false; + for (fork_set.keys()) |*fork| { + if (fork.uses == 0) { + std.log.err("fork {f} matched no {s} packages", .{ + fork.path, fork.manifest.name, + }); + any_unused = true; + } else { + std.log.info("fork {f} matched {d} {s} packages", .{ + fork.path, fork.uses, fork.manifest.name, + }); + } + } + if (any_unused) process.exit(1); + } + + try job_queue.consolidateErrors(); + + if (fetch.error_bundle.root_list.items.len > 0) { + var errors = try fetch.error_bundle.toOwnedBundle(""); + // TODO when watching, watch and rebuild configure script rather than exit here + errors.renderToStderr(io, .{}, color) catch {}; + process.exit(1); + } + + if (fetch_only) return cleanExit(io); + + // Create the dependencies.zig file for configurer to + // obtain via `@import("@dependencies")`. + { + { + dependencies_source.clearRetainingCapacity(); + var source_writer: Io.Writer.Allocating = .fromArrayList(&dependencies_source); + defer dependencies_source = source_writer.toArrayList(); + job_queue.createDependenciesSource(&dependencies_source) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + }; + } + // Atomically create the file in a directory named after the hash of its contents. + var hh: Cache.HashHelper = .{}; + hh.addBytes(builtin.zig_version_string); + hh.addBytes(dependencies_source.items); + const hex_digest = hh.final(); + const dependencies_zig_path: Path = .{ + .root_dir = graph.local_cache_root, + .sub_path = try allocPrint(arena, "o/{s}/dependencies.zig", .{ &hex_digest }), + }; + var atomic_file = try dependencies_zig_path.root_dir.handle.createFileAtomic( + io, + dependencies_zig_path.sub_path, .{ .make_path = true, .replace = true }, + ); + defer atomic_file.deinit(io); + atomic_file.file.writeStreamingAll(io, dependencies_source.items) catch |err| + fatal("writing dependencies.zig contents: {t}", .{err}); + atomic_file.replace(io) catch |err| + fatal("replacing {f}: {t}", .{dependencies_zig_path, err}); + + deps_mod.root_path = try dependencies_zig_path.toString(arena); + } + + { + // Add a CliModule for each package's build.zig. + const hashes = job_queue.table.keys(); + const fetches = job_queue.table.values(); + try deps_mod.deps.ensureUnusedCapacity(gpa, @intCast(hashes.len)); + for (hashes, fetches) |*hash, f| { + if (f == &fetch) { + // The first one is a dummy package for the current project. + continue; + } + if (!f.has_build_zig) + continue; + const hash_slice = try arena.dupe(u8, hash.toSlice()); + + const m = try arena.create(CliModule); + m.* = .{ + .root_path = try f.package_root.toString(arena), + .name = hash_slice, + }; + deps_mod.deps.putAssumeCapacityNoClobber(hash_slice, m); + f.cli_module = m; + } + + // Each build.zig module needs access to each of its + // dependencies' build.zig modules by name. + for (fetches) |f| { + const mod = f.cli_module orelse continue; + if (!f.have_manifest) continue; + const man = &f.manifest; + const dep_names = man.dependencies.keys(); + try mod.deps.ensureUnusedCapacity(gpa, @intCast(dep_names.len)); + for (dep_names, man.dependencies.values()) |name, dep| { + const dep_digest = Package.Fetch.depDigest( + f.package_root, + global_cache_directory, + dep, + ) orelse continue; + const dep_mod = job_queue.table.get(dep_digest).?.module orelse continue; + const name_cloned = try arena.dupe(u8, name); + mod.deps.putAssumeCapacityNoClobber(name_cloned, dep_mod); + } + } + } + + // Lower module dependencies to CLI argv. + build_configurer_argv.shrinkRetainingCapacity(argv_deps_index); + for (deps_mod.deps.values()) |dep| { + try build_configurer_argv.ensureUnusedCapacity(gpa, 2 * dep.deps.count() + 1); + for (dep.deps.values()) |sub| { + build_configurer_argv.appendAssumeCapacity("--dep"); + build_configurer_argv.appendAssumeCapacity(sub.name); + } + build_configurer_argv.appendAssumeCapacity(try allocPrint(arena, "-M{s}={s}", .{ + dep.name, dep.root_path, + })); + } + try build_configurer_argv.ensureUnusedCapacity(gpa, 2 * deps_mod.deps.count() + 1); + for (deps_mod.deps.values()) |dep| { + build_configurer_argv.appendAssumeCapacity("--dep"); + build_configurer_argv.appendAssumeCapacity(dep.name); + } + build_configurer_argv.appendAssumeCapacity(try allocPrint(arena, "-M@dependencies={s}", .{ + deps_mod.root_path, + })); + } + + const compile_prog_node = main_progress_node.start("Compile Configure Script", 0); + defer compile_prog_node.end(); + + try build_configurer_argv.append(gpa, "--listen=-"); + + file_system_inputs.clearRetainingCapacity(); + execute_child(build_configurer_argv, &file_system_inputs); + + const hex_digest: []const u8 = &Cache.binToHex(comp.digest.?); + const exe_path: Path = .{ + .root_dir = dirs.local_cache, + .sub_path = try allocPrint(arena, "o/{s}/{s}", .{ hex_digest, comp.emit_bin.? }), + }; + _ = try config_man.addFilePath(exe_path, null); + configure_argv.items[0] = try exe_path.toString(arena); + + switch (cache_poison) { + .pure, .disallowed, .ignored => if (try config_man.hit()) { + const digest = config_man.final(); + break :cp .{ + .{ + .root_dir = dirs.local_cache, + .sub_path = try allocPrint(arena, "c/{s}", .{&digest}), + }, + false, + }; + }, + .poisoned => {}, // Don't bother checking for cache hit. + } + } + + if (!process.can_spawn) { + const cmd = try std.mem.join(arena, " ", configure_argv.items); + fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ native_os, cmd }); + } + + const rand_int = randInt(io, u64); + const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); + const config_tmp_path: Path = .{ + .root_dir = dirs.local_cache, + .sub_path = tmp_dir_sub_path, + }; + const config_tmp_file: Io.File = try config_tmp_path.root_dir.handle.createFile( + io, + config_tmp_path.sub_path, + .{ .read = true, .exclusive = true }, + ); + defer config_tmp_file.close(io); + + const term = term: { + const child_node = main_progress_node.start("Run Configure Script", 0); + defer child_node.end(); + var child = std.process.spawn(io, .{ + .argv = configure_argv.items, + .stdout = .{ .file = config_tmp_file }, + .progress_node = child_node, + }) catch |err| fatal("failed to spawn configure script {q}: {t}", .{ configure_argv.items[0], err }); + defer child.kill(io); + break :term child.wait(io) catch |err| + fatal("failed to wait configure script {q}: {t}", .{ configure_argv.items[0], err }); + }; + if (!term.success()) { + // Failure to produce the configuration file. + const cmd = try std.mem.join(arena, " ", configure_argv.items); + fatal("the following configure command {f}:\n{s}", .{ term, cmd }); + } + // Even though the file is designed to be sent directly to make + // runner, we must load it now because: + // * If it contains additional file dependencies, we need to + // add them to `config_man` before obtaining the final digest. + // * If it contains a set of lazy packages that need to be + // fetched, we need to fetch those now and re-run configure. + var configuration = std.Build.Configuration.loadFile(arena, io, config_tmp_file) catch |err| + fatal("failed to load configuration file {f}: {t}", .{ config_tmp_path, err }); + + if (configuration.unlazy_deps.len != 0) { + if (!dev.env.supports(.fetch_command)) process.exit(1); + var any_errors = false; + for (configuration.unlazy_deps) |hash_string| { + const hash = hash_string.slice(&configuration); + assert(hash.len != 0); + if (hash.len > Package.Hash.max_len) { + std.log.err("invalid digest (length {d} exceeds maximum): {q}", .{ hash.len, hash }); + any_errors = true; + continue; + } + try unlazy_set.put(arena, .fromSlice(hash), {}); + } + if (any_errors) process.exit(1); + if (system_pkg_dir_path) |p| { + // In this mode, the system needs to provide these packages; they + // cannot be fetched by Zig. + const s = fs.path.sep_str; + for (unlazy_set.keys()) |*hash| { + std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ p, hash.toSlice() }); + } + std.log.info("remote package fetching disabled due to --system mode", .{}); + std.log.info("dependencies might be avoidable depending on build configuration", .{}); + process.exit(1); + } + continue :cp; + } + + for (configuration.path_deps_base, configuration.path_deps_sub) |base, sub| { + const conf_path: std.Build.Configuration.Path = .{ .base = base, .sub = sub }; + try config_man.addPathPost(conf_path.toCachePath(&configuration, arena)); + } + + // We need to add to the configuration cache the source files of + // configurer itself, so that the maker process can watch the file system + // for those changes and restart itself. By doing this, we make it + // possible to bypass creating a Compilation for configurer on + // Configuration cache hit. + { + var it = mem.splitScalar(u8, file_system_inputs.items, 0); + while (it.next()) |input| { + _ = try config_man.addPrefixedPathPost(.{ + .prefix = input[0], + .sub_path = input[1..], + }); + } + } + + // If it is poisoned, there is no point in moving it to cached + // location. Just leave it in the tmp directory. + if (configuration.poisoned) { + break :cp .{ config_tmp_path, true }; + } else { + const digest = config_man.final(); + const final_path: Path = .{ + .root_dir = dirs.local_cache, + .sub_path = try allocPrint(arena, "c/{s}", .{&digest}), + }; + Io.Dir.rename( + config_tmp_path.root_dir.handle, + config_tmp_path.sub_path, + final_path.root_dir.handle, + final_path.sub_path, + io, + ) catch |err| retry: { + const e = switch (err) { + error.FileNotFound => e: { + const dir_path = final_path.dirname().?; + dir_path.root_dir.handle.createDirPath(io, dir_path.sub_path) catch |e| + fatal("failed to create directory {f}: {t}", .{ dir_path, e }); + if (Io.Dir.rename( + config_tmp_path.root_dir.handle, + config_tmp_path.sub_path, + final_path.root_dir.handle, + final_path.sub_path, + io, + )) |_| break :retry else |e| break :e e; + }, + else => |e| e, + }; + fatal("failed to rename configuration file from {f} into {f}: {t}", .{ + config_tmp_path, final_path, e, + }); + }; + config_man.writeManifest() catch |err| warn("failed to write cache manifest: {t}", .{err}); + break :cp .{ final_path, false }; + } + }; + + { + // Release all file system locks just before running the maker process. + var configuration_lock = if (!poisoned) config_man.toOwnedLock() else null; + defer if (configuration_lock) |*l| l.release(io); + + if (print_configuration_path) { + var stdout_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer); + stdout_writer.interface.print("{f}\n", .{configuration_path}) catch + fatal("failed printing cache file path: {t}", .{stdout_writer.err.?}); + stdout_writer.flush() catch |err| + fatal("failed printing cache file path: {t}", .{err}); + return cleanExit(io); + } + const make_runner = make_runner_task.await(io) catch |err| fatal("failed compiling maker: {t}", .{err}); + + make_argv.items[0] = try make_runner.exe_path.toString(arena); + make_argv.items[argv_index_configuration_file] = try configuration_path.toString(arena); + } + } + const scanned_config: ScannedConfig = sc: { const configuration = c: { var file = cwd.openFile(io, configure_path, .{}) catch |err| @@ -505,7 +1162,7 @@ pub fn main(init: process.Init.Minimal) !void { }; if (help_menu) { - var w = initStdoutWriter(io); + const w = initStdoutWriter(io); scanned_config.printUsage(&graph, w) catch |err| switch (err) { error.WriteFailed => return stdout_writer_allocation.err.?, else => |e| return e, @@ -513,18 +1170,24 @@ pub fn main(init: process.Init.Minimal) !void { w.flush() catch return stdout_writer_allocation.err.?; return cleanExit(io, &scanned_config); } else if (steps_menu) { - var w = initStdoutWriter(io); + const w = initStdoutWriter(io); scanned_config.printSteps(&graph, w) catch |err| switch (err) { error.WriteFailed => return stdout_writer_allocation.err.?, else => |e| return e, }; w.flush() catch return stdout_writer_allocation.err.?; return cleanExit(io, &scanned_config); - } else if (print_configuration) { - var w = initStdoutWriter(io); - scanned_config.print(w) catch return stdout_writer_allocation.err.?; - w.flush() catch return stdout_writer_allocation.err.?; - return cleanExit(io, &scanned_config); + } else switch (print_configuration) { + .none => {}, + .zon => { + const w = initStdoutWriter(io); + scanned_config.print(w) catch return stdout_writer_allocation.err.?; + w.flush() catch return stdout_writer_allocation.err.?; + return cleanExit(io, &scanned_config); + }, + .path => { + @panic("TODO"); + }, } if (webui_listen != null) { @@ -532,11 +1195,6 @@ pub fn main(init: process.Init.Minimal) !void { if (builtin.single_threaded) fatal("'--webui' is not yet supported on single-threaded hosts", .{}); } - 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" }), @@ -706,6 +1364,338 @@ pub fn main(init: process.Init.Minimal) !void { } } +fn cmdFetch( + gpa: Allocator, + graph: *Graph, + args: []const []const u8 +) !void { + const environ_map = &graph.environ_map; + const io = graph.io; + const arena = graph.arena; + + const color: Color = Color.settingFromEnvironment(environ_map); + var opt_path_or_url: ?[]const u8 = null; + var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map); + var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(environ_map); + var override_pkg_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_PKG_DIR.get(environ_map); + var debug_hash: bool = false; + var save: union(enum) { + no, + yes: ?[]const u8, + exact: ?[]const u8, + } = .no; + + var arg_i: usize = 0; + while (nextArg(args, &arg_i)) |arg| { + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { + try Io.File.stdout().writeStreamingAll(io, usage_fetch); + return cleanExit(io); + } else if (mem.eql(u8, arg, "--global-cache-dir")) { + override_global_cache_dir = nextArgOrFatal(args, &arg_i); + } 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, "--debug-hash")) { + debug_hash = true; + } 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, "--save")) { + save = .{ .yes = null }; + } else if (mem.cutPrefix(u8, arg, "--save=")) |rest| { + save = .{ .yes = rest }; + } else if (mem.eql(u8, arg, "--save-exact")) { + save = .{ .exact = null }; + } else if (mem.cutPrefix(u8, arg, "--save-exact=")) |rest| { + save = .{ .exact = rest }; + } else { + fatal("unrecognized parameter: {q}", .{arg}); + } + } else if (opt_path_or_url != null) { + fatal("unexpected extra parameter: {q}", .{arg}); + } else { + opt_path_or_url = arg; + } + } + + const path_or_url = opt_path_or_url orelse fatal("missing url or path parameter", .{}); + + var http_client: std.http.Client = .{ .allocator = gpa, .io = io }; + defer http_client.deinit(); + + try http_client.initDefaultProxies(arena, environ_map); + + var root_prog_node = std.Progress.start(io, .{ + .root_name = "Fetch", + }); + defer root_prog_node.end(); + + var local_storage: Fetch.LocalStorage = undefined; + var build_root: BuildRoot = undefined; + var build_root_initialized = false; + defer if (build_root_initialized) build_root.deinit(io); + + const cwd_path = try std.zig.getResolvedCwd(io, arena); + + const local_storage_ptr = switch (save) { + .no => null, + .yes, .exact => ls: { + build_root = try findBuildRoot(arena, io, .{ .cwd_path = cwd_path }); + build_root_initialized = true; + + local_storage = .{ + .cache_root = if (override_local_cache_dir) |p| .initCwd(p) else .{ + .root_dir = build_root.directory, + .sub_path = ".zig-cache", + }, + .pkg_root = if (override_pkg_dir) |p| .initCwd(p) else .{ + .root_dir = build_root.directory, + .sub_path = "zig-pkg", + }, + }; + + break :ls &local_storage; + }, + }; + + var job_queue: Fetch.JobQueue = .{ + .io = io, + .http_client = &http_client, + .global_cache = graph.global_cache_root, + .local_storage = local_storage_ptr, + .recursive = false, + .read_only = false, + .debug_hash = debug_hash, + .mode = .all, + .prog_node = root_prog_node, + }; + defer job_queue.deinit(); + + var fetch: Fetch = .{ + .arena = std.heap.ArenaAllocator.init(gpa), + .location = .{ .path_or_url = path_or_url }, + .location_tok = 0, + .hash_tok = .none, + .name_tok = 0, + .lazy_status = .eager, + .remote_package_root = undefined, + .parent_package_root = undefined, + .parent_manifest_ast = null, + .prog_node = root_prog_node, + .job_queue = &job_queue, + .omit_missing_hash_error = true, + .allow_missing_paths_field = false, + .use_latest_commit = true, + + .package_root = undefined, + .error_bundle = undefined, + .manifest = undefined, + .manifest_ast = undefined, + .have_manifest = false, + .computed_hash = undefined, + .has_build_zig = false, + .oom_flag = false, + .latest_commit = null, + + .module = null, + }; + defer fetch.deinit(); + + fetch.run() catch |err| switch (err) { + error.OutOfMemory, error.Canceled => |e| return e, + error.FetchFailed => {}, // error bundle checked below + }; + + try job_queue.group.await(io); + + if (fetch.error_bundle.root_list.items.len > 0) { + var errors = try fetch.error_bundle.toOwnedBundle(""); + errors.renderToStderr(io, .{}, color) catch {}; + process.exit(1); + } + + const package_hash = fetch.computedPackageHash(); + const package_hash_slice = package_hash.toSlice(); + + root_prog_node.end(); + root_prog_node = .{ .index = .none }; + + const name = switch (save) { + .no => { + var data: [2][]const u8 = .{ package_hash_slice, "\n" }; + const w = initStdoutWriter(); + try w.writeVecAll(&data); + try w.flush(); + return cleanExit(io); + }, + .yes, .exact => |name| name: { + if (name) |n| break :name n; + if (!fetch.have_manifest) + fatal("unable to determine name; fetched package has no build.zig.zon file", .{}); + break :name fetch.manifest.name; + }, + }; + + // The name to use in case the manifest file needs to be created now. + const init_root_name = Dir.path.basename(build_root.directory.path orelse cwd_path); + var manifest, var ast = try loadManifest(gpa, arena, io, .{ + .root_name = try sanitizeExampleName(arena, init_root_name), + .dir = build_root.directory.handle, + .color = color, + }); + defer { + manifest.deinit(gpa); + ast.deinit(gpa); + } + + var fixups: Ast.Render.Fixups = .{}; + defer fixups.deinit(gpa); + + var saved_path_or_url = path_or_url; + + if (fetch.latest_commit) |latest_commit| resolved: { + const latest_commit_hex = try allocPrint(arena, "{f}", .{latest_commit}); + + var uri = try std.Uri.parse(path_or_url); + + if (uri.fragment) |fragment| { + const target_ref = try fragment.toRawMaybeAlloc(arena); + + // the refspec may already be fully resolved + if (std.mem.eql(u8, target_ref, latest_commit_hex)) break :resolved; + + std.log.info("resolved ref {q} to commit {s}", .{ target_ref, latest_commit_hex }); + + // include the original refspec in a query parameter, could be used to check for updates + uri.query = .{ .percent_encoded = try allocPrint(arena, "ref={f}", .{ + std.fmt.alt(fragment, .formatEscaped), + }) }; + } else { + std.log.info("resolved to commit {s}", .{latest_commit_hex}); + } + + // replace the refspec with the resolved commit SHA + uri.fragment = .{ .raw = latest_commit_hex }; + + switch (save) { + .yes => saved_path_or_url = try allocPrint(arena, "{f}", .{uri}), + .no, .exact => {}, // keep the original URL + } + } + + const new_node_init = try allocPrint(arena, + \\.{{ + \\ .url = "{f}", + \\ .hash = "{f}", + \\ }} + , .{ + std.zig.fmtString(saved_path_or_url), + std.zig.fmtString(package_hash_slice), + }); + + const new_node_text = try allocPrint(arena, ".{f} = {s},\n", .{ + std.zig.fmtIdPU(name), new_node_init, + }); + + const dependencies_init = try allocPrint(arena, ".{{\n {s} }}", .{ + new_node_text, + }); + + const dependencies_text = try allocPrint(arena, ".dependencies = {s},\n", .{ + dependencies_init, + }); + + if (manifest.dependencies.get(name)) |dep| { + if (dep.hash) |h| { + switch (dep.location) { + .url => |u| { + if (mem.eql(u8, h, package_hash_slice) and mem.eql(u8, u, saved_path_or_url)) { + std.log.info("existing dependency named {q} is up-to-date", .{name}); + process.exit(0); + } + }, + .path => {}, + } + } + + const location_replace = try allocPrint( + arena, + "\"{f}\"", + .{std.zig.fmtString(saved_path_or_url)}, + ); + const hash_replace = try allocPrint( + arena, + "\"{f}\"", + .{std.zig.fmtString(package_hash_slice)}, + ); + + warn("overwriting existing dependency named {q}", .{name}); + try fixups.replace_nodes_with_string.put(gpa, dep.location_node, location_replace); + if (dep.hash_node.unwrap()) |hash_node| { + try fixups.replace_nodes_with_string.put(gpa, hash_node, hash_replace); + } else { + // https://github.com/ziglang/zig/issues/21690 + } + } else if (manifest.dependencies.count() > 0) { + // Add fixup for adding another dependency. + const deps = manifest.dependencies.values(); + const last_dep_node = deps[deps.len - 1].node; + try fixups.append_string_after_node.put(gpa, last_dep_node, new_node_text); + } else if (manifest.dependencies_node.unwrap()) |dependencies_node| { + // Add fixup for replacing the entire dependencies struct. + try fixups.replace_nodes_with_string.put(gpa, dependencies_node, dependencies_init); + } else { + // Add fixup for adding dependencies struct. + try fixups.append_string_after_node.put(gpa, manifest.version_node, dependencies_text); + } + + var aw: Io.Writer.Allocating = .init(gpa); + defer aw.deinit(); + try ast.render(gpa, &aw.writer, fixups); + const rendered = aw.written(); + + build_root.directory.handle.writeFile(io, .{ .sub_path = Package.Manifest.basename, .data = rendered }) catch |err| { + fatal("unable to write {s} file: {t}", .{ Package.Manifest.basename, err }); + }; + + return cleanExit(io); +} + +const usage_fetch = + \\Usage: zig fetch [options] + \\Usage: zig fetch [options] + \\ + \\ Copy a package into the global cache and print its hash. + \\ must point to one of the following: + \\ - A git+http / git+https server for the package + \\ - A tarball file (with or without compression) containing + \\ package source + \\ - A git bundle file containing package source + \\ + \\Examples: + \\ + \\ zig fetch --save git+https://example.com/andrewrk/fun-example-tool.git + \\ zig fetch --save https://example.com/andrewrk/fun-example-tool/archive/refs/heads/master.tar.gz + \\ + \\Options: + \\ -h, --help Print this help and exit + \\ --global-cache-dir [path] Override path to global Zig cache directory + \\ --cache-dir [path] Override path to local cache directory + \\ --pkg-dir [path] Override path to local package directory + \\ --debug-hash Print verbose hash information to stdout + \\ --debug-log [scope] Enable printing debug/info log messages for scope + \\ --save Add the fetched package to build.zig.zon + \\ --save=[name] Add the fetched package to build.zig.zon as name + \\ --save-exact Add the fetched package to build.zig.zon, storing the URL verbatim + \\ --save-exact=[name] Add the fetched package to build.zig.zon as name, storing the URL verbatim + \\ +; + +fn cmdBuild() !void { + +} + fn markFailedStepsDirty(maker: *Maker) void { const all_steps = maker.step_stack.keys(); @@ -1677,16 +2667,13 @@ fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 { } fn nextArgOrFatal(args: []const [:0]const u8, idx: *usize) [:0]const u8 { - return nextArg(args, idx) orelse { - fatalWithHint("expected argument after {q}", .{args[idx.* - 1]}); - }; + return nextArg(args, idx) orelse fatalWithHint("expected argument after {q}", .{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; +fn prefixedArgOrFatal(args: []const [:0]const u8, index_ptr: *usize, prefix: []const u8) []const u8 { + const arg = args[index_ptr.*]; + if (mem.cutPrefix(u8, arg, prefix)) |rest| return rest; + fatal("expected {q} to begin with {q}", .{arg, prefix}); } fn argsRest(args: []const [:0]const u8, idx: usize) ?[]const [:0]const u8 { @@ -2006,11 +2993,11 @@ pub fn installSymLinks( const name = conf_comp.root_name.slice(c); const filename_major_only, const filename_name_only = if (os_tag.isDarwin()) .{ - try std.fmt.allocPrint(arena, "lib{s}.{d}.dylib", .{ name, version.major }), - try std.fmt.allocPrint(arena, "lib{s}.dylib", .{name}), + try allocPrint(arena, "lib{s}.{d}.dylib", .{ name, version.major }), + try allocPrint(arena, "lib{s}.dylib", .{name}), } else .{ - try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ name, version.major }), - try std.fmt.allocPrint(arena, "lib{s}.so", .{name}), + try allocPrint(arena, "lib{s}.so.{d}", .{ name, version.major }), + try allocPrint(arena, "lib{s}.so", .{name}), }; return installSymLinksInner(maker, arena, output_path, asking_step_index, filename_major_only, filename_name_only); @@ -2059,3 +3046,229 @@ inline fn debugMakerLeaks() bool { if (!is_debug_mode) return false; return debug_maker_leaks; } + +const BuildRoot = struct { + directory: Cache.Directory, + build_zig_basename: []const u8, + cleanup_build_dir: ?Io.Dir, + + fn deinit(br: *BuildRoot, io: Io) void { + if (br.cleanup_build_dir) |*dir| dir.close(io); + br.* = undefined; + } +}; + +const FindBuildRootOptions = struct { + build_file: ?[]const u8 = null, + cwd_path: ?[]const u8 = null, +}; + +fn findBuildRoot(arena: Allocator, io: Io, options: FindBuildRootOptions) !BuildRoot { + const cwd_path = options.cwd_path orelse try std.zig.getResolvedCwd(io, arena); + const build_zig_basename = if (options.build_file) |bf| + Dir.path.basename(bf) + else + std.zig.build_zig_basename; + + if (options.build_file) |bf| { + if (Dir.path.dirname(bf)) |dirname| { + const dir = Io.Dir.cwd().openDir(io, dirname, .{}) catch |err| { + fatal("failed opening directory containing {q}: {t}", .{ bf, err }); + }; + return .{ + .build_zig_basename = build_zig_basename, + .directory = .{ .path = dirname, .handle = dir }, + .cleanup_build_dir = dir, + }; + } + + return .{ + .build_zig_basename = build_zig_basename, + .directory = .{ .path = null, .handle = Io.Dir.cwd() }, + .cleanup_build_dir = null, + }; + } + // Search up parent directories until we find build.zig. + var dirname: []const u8 = cwd_path; + while (true) { + const joined_path = try Dir.path.join(arena, &[_][]const u8{ dirname, build_zig_basename }); + if (Io.Dir.cwd().access(io, joined_path, .{})) |_| { + const dir = Io.Dir.cwd().openDir(io, dirname, .{}) catch |err| { + fatal("unable to open directory while searching for build.zig file, {q}: {t}", .{ dirname, err }); + }; + return .{ + .build_zig_basename = build_zig_basename, + .directory = .{ + .path = dirname, + .handle = dir, + }, + .cleanup_build_dir = dir, + }; + } else |err| switch (err) { + error.FileNotFound => { + dirname = Dir.path.dirname(dirname) orelse { + std.log.info("initialize {s} template file with \"zig init\"", .{ std.zig.build_zig_basename }); + std.log.info("see \"zig --help\" for more options", .{}); + fatal("no build.zig file found, in the current directory or any parent directories", .{}); + }; + continue; + }, + else => |e| return e, + } + } +} + +const Fork = struct { + path: Path, + manifest_ast: std.zig.Ast, + manifest: Package.Manifest, + error_bundle: std.zig.ErrorBundle.Wip, + failed: bool, + arena_allocator: std.heap.ArenaAllocator, + + fn init(cwd_relative_path: []const u8) Fork { + return .{ + .manifest_ast = undefined, + .manifest = undefined, + .error_bundle = undefined, + .arena_allocator = undefined, + .path = .{ + .root_dir = .cwd(), + .sub_path = cwd_relative_path, + }, + .failed = false, + }; + } + + fn load(io: Io, gpa: Allocator, fork: *Fork, color: Color) Io.Cancelable!void { + loadFallible(io, gpa, fork, color) catch |err| switch (err) { + error.Canceled => |e| return e, + error.AlreadyReported => fork.failed = true, + else => |e| { + std.log.err("failed to load fork at {f}: {t}", .{ fork.path, e }); + fork.failed = true; + }, + }; + } + + fn loadFallible(io: Io, gpa: Allocator, fork: *Fork, color: Color) !void { + fork.arena_allocator = .init(gpa); + const arena = fork.arena_allocator.allocator(); + + var error_bundle: std.zig.ErrorBundle.Wip = undefined; + try error_bundle.init(gpa); + defer error_bundle.deinit(); + + const manifest_path = try fork.path.join(arena, Package.Manifest.basename); + + Package.Manifest.load( + io, + arena, + manifest_path, + &fork.manifest_ast, + &error_bundle, + &fork.manifest, + true, + ) catch |err| switch (err) { + error.Canceled => |e| return e, + error.ErrorsBundled => { + assert(error_bundle.root_list.items.len > 0); + var errors = try error_bundle.toOwnedBundle(""); + errors.renderToStderr(io, .{}, color) catch {}; + return error.AlreadyReported; + }, + else => |e| { + std.log.err("failed to load package manifest {f}: {t}", .{ manifest_path, e }); + return error.AlreadyReported; + }, + }; + } + + fn deinitList(forks: []Fork) void { + for (forks) |*fork| fork.arena_allocator.deinit(); + } +}; + +fn parseRandomSeed(arg: []const u8) u32 { + return std.fmt.parseUnsigned(u32, arg, 0) catch |err| + fatal("failed parsing random seed {q} as unsigned 32-bit integer: {t}", .{ arg, err }); +} + +fn randInt(io: Io, comptime T: type) T { + var x: T = undefined; + io.random(@ptrCast(&x)); + return x; +} + +const LoadManifestOptions = struct { + root_name: []const u8, + dir: Io.Dir, + color: Color, +}; + +fn loadManifest( + gpa: Allocator, + arena: Allocator, + io: Io, + options: LoadManifestOptions, +) !struct { Package.Manifest, std.zig.Ast } { + const rng: std.Random.IoSource = .{ .io = io }; + + const manifest_bytes = while (true) { + break options.dir.readFileAllocOptions( + io, + Package.Manifest.basename, + arena, + .limited(Package.Manifest.max_bytes), + .@"1", + 0, + ) catch |err| switch (err) { + error.FileNotFound => { + writeSimpleTemplateFile(io, Package.Manifest.basename, + \\.{{ + \\ .name = .{s}, + \\ .version = "{s}", + \\ .paths = .{{""}}, + \\ .fingerprint = 0x{x}, + \\}} + \\ + , .{ + options.root_name, + build_options.version, + Package.Fingerprint.generate(rng.interface(), options.root_name).int(), + }) catch |e| { + fatal("unable to write {s}: {t}", .{ Package.Manifest.basename, e }); + }; + continue; + }, + else => |e| fatal("unable to load {s}: {t}", .{ Package.Manifest.basename, e }), + }; + }; + var ast = try Ast.parse(gpa, manifest_bytes, .zon); + errdefer ast.deinit(gpa); + + if (ast.errors.len > 0) { + try std.zig.printAstErrorsToStderr(gpa, io, ast, Package.Manifest.basename, options.color); + process.exit(2); + } + + var manifest = try Package.Manifest.parse(gpa, &ast, rng.interface(), .{}); + errdefer manifest.deinit(gpa); + + if (manifest.errors.len > 0) { + var wip_errors: std.zig.ErrorBundle.Wip = undefined; + try wip_errors.init(gpa); + defer wip_errors.deinit(); + + const src_path = try wip_errors.addString(Package.Manifest.basename); + try manifest.copyErrorsIntoBundle(ast, src_path, &wip_errors); + + var error_bundle = try wip_errors.toOwnedBundle(""); + defer error_bundle.deinit(gpa); + error_bundle.renderToStderr(io, .{}, options.color) catch {}; + + process.exit(2); + } + return .{ manifest, ast }; +} + diff --git a/src/Package/Fetch.zig b/lib/compiler/Maker/Fetch.zig similarity index 99% rename from src/Package/Fetch.zig rename to lib/compiler/Maker/Fetch.zig index 20e19cdaa7ad2e55b9ddb07f490df1ffdaa26eea..b6093f59b6992efba93f8327348b1145c905c41b 100644 --- a/src/Package/Fetch.zig +++ b/lib/compiler/Maker/Fetch.zig @@ -96,7 +96,10 @@ latest_commit: ?git.Oid, /// The module for this `Fetch` tasks's package, which exposes `build.zig` as /// the root source file. -module: ?*Package.Module, +/// +/// This could be an opaque "userdata" field because this code does not observe +/// this data in any way but let's have some type safety because we can. +cli_module: ?*@import("../Maker.zig").CliModule, pub const LazyStatus = enum { /// Not lazy. @@ -227,16 +230,16 @@ pub const JobQueue = struct { /// Creates the dependencies.zig source code for the build runner to obtain /// via `@import("@dependencies")`. - pub fn createDependenciesSource(jq: *JobQueue, buf: *std.array_list.Managed(u8)) Allocator.Error!void { + pub fn createDependenciesSource(jq: *JobQueue, w: *Io.Writer) Io.Writer.Error!void { const keys = jq.table.keys(); assert(keys.len != 0); // caller should have added the first one if (keys.len == 1) { // This is the first one. It must have no dependencies. - return createEmptyDependenciesSource(buf); + return createEmptyDependenciesSource(w); } - try buf.appendSlice("pub const packages = struct {\n"); + try w.writeAll("pub const packages = struct {\n"); // Ensure the generated .zig file is deterministic. jq.table.sortUnstable(@as(struct { @@ -254,7 +257,7 @@ pub const JobQueue = struct { const hash_slice = hash.toSlice(); - try buf.print( + try w.print( \\ pub const {f} = struct {{ \\ , .{std.zig.fmtId(hash_slice)}); @@ -263,14 +266,14 @@ pub const JobQueue = struct { switch (fetch.lazy_status) { .eager => break :lazy, .available => { - try buf.appendSlice( + try w.writeAll( \\ pub const available = true; \\ ); break :lazy; }, .unavailable => { - try buf.appendSlice( + try w.writeAll( \\ pub const available = false; \\ }; \\ @@ -280,13 +283,13 @@ pub const JobQueue = struct { } } - try buf.print( + try w.print( \\ pub const build_root = "{f}"; \\ , .{std.fmt.alt(fetch.package_root, .formatEscapeString)}); if (fetch.has_build_zig) { - try buf.print( + try w.print( \\ pub const build_zig = @import("{f}"); \\ , .{std.zig.fmtString(hash_slice)}); @@ -294,25 +297,25 @@ pub const JobQueue = struct { if (fetch.have_manifest) { const manifest = &fetch.manifest; - try buf.appendSlice( + try w.writeAll( \\ pub const deps: []const struct { []const u8, []const u8 } = &.{ \\ ); for (manifest.dependencies.keys(), manifest.dependencies.values()) |name, dep| { const h = depDigest(fetch.package_root, jq.global_cache, dep) orelse continue; - try buf.print( + try w.print( " .{{ \"{f}\", \"{f}\" }},\n", .{ std.zig.fmtString(name), std.zig.fmtString(h.toSlice()) }, ); } - try buf.appendSlice( + try w.writeAll( \\ }; \\ }; \\ ); } else { - try buf.appendSlice( + try w.writeAll( \\ pub const deps: []const struct { []const u8, []const u8 } = &.{}; \\ }; \\ @@ -320,7 +323,7 @@ pub const JobQueue = struct { } } - try buf.appendSlice( + try w.writeAll( \\}; \\ \\pub const root_deps: []const struct { []const u8, []const u8 } = &.{ @@ -333,16 +336,16 @@ pub const JobQueue = struct { for (root_manifest.dependencies.keys(), root_manifest.dependencies.values()) |name, dep| { const h = depDigest(root_fetch.package_root, jq.global_cache, dep) orelse continue; - try buf.print( + try w.print( " .{{ \"{f}\", \"{f}\" }},\n", .{ std.zig.fmtString(name), std.zig.fmtString(h.toSlice()) }, ); } - try buf.appendSlice("};\n"); + try w.appendSlice("};\n"); } - pub fn createEmptyDependenciesSource(buf: *std.array_list.Managed(u8)) Allocator.Error!void { - try buf.appendSlice( + pub fn createEmptyDependenciesSource(w: *Io.Writer) Io.Writer!void { + try w.writeAll( \\pub const packages = struct {}; \\pub const root_deps: []const struct { []const u8, []const u8 } = &.{}; \\ @@ -1020,7 +1023,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void { .oom_flag = false, .latest_commit = null, - .module = null, + .cli_module = null, }; } diff --git a/src/Package/Fetch/git.zig b/lib/compiler/Maker/Fetch/git.zig similarity index 100% rename from src/Package/Fetch/git.zig rename to lib/compiler/Maker/Fetch/git.zig diff --git a/src/Package/Fetch/git/testdata/testrepo-sha1.idx b/lib/compiler/Maker/Fetch/git/testdata/testrepo-sha1.idx similarity index 100% rename from src/Package/Fetch/git/testdata/testrepo-sha1.idx rename to lib/compiler/Maker/Fetch/git/testdata/testrepo-sha1.idx diff --git a/src/Package/Fetch/git/testdata/testrepo-sha1.pack b/lib/compiler/Maker/Fetch/git/testdata/testrepo-sha1.pack similarity index 100% rename from src/Package/Fetch/git/testdata/testrepo-sha1.pack rename to lib/compiler/Maker/Fetch/git/testdata/testrepo-sha1.pack diff --git a/src/Package/Fetch/git/testdata/testrepo-sha256.idx b/lib/compiler/Maker/Fetch/git/testdata/testrepo-sha256.idx similarity index 100% rename from src/Package/Fetch/git/testdata/testrepo-sha256.idx rename to lib/compiler/Maker/Fetch/git/testdata/testrepo-sha256.idx diff --git a/src/Package/Fetch/git/testdata/testrepo-sha256.pack b/lib/compiler/Maker/Fetch/git/testdata/testrepo-sha256.pack similarity index 100% rename from src/Package/Fetch/git/testdata/testrepo-sha256.pack rename to lib/compiler/Maker/Fetch/git/testdata/testrepo-sha256.pack diff --git a/src/Package.zig b/lib/compiler/Maker/Package.zig similarity index 98% rename from src/Package.zig rename to lib/compiler/Maker/Package.zig index 8fb9995bd81315343e9b1da8d1741774ae4e9d82..01bcf01036acc109c288727cf6099e9f0d65e94c 100644 --- a/src/Package.zig +++ b/lib/compiler/Maker/Package.zig @@ -1,9 +1,7 @@ const std = @import("std"); const assert = std.debug.assert; -pub const Module = @import("Package/Module.zig"); pub const Fetch = @import("Package/Fetch.zig"); -pub const build_zig_basename = "build.zig"; pub const Manifest = @import("Package/Manifest.zig"); pub const Fingerprint = packed struct(u64) { diff --git a/src/Package/Manifest.zig b/lib/compiler/Maker/Package/Manifest.zig similarity index 100% rename from src/Package/Manifest.zig rename to lib/compiler/Maker/Package/Manifest.zig diff --git a/lib/compiler/configurer.zig b/lib/compiler/configurer.zig index 809bb9e6263c3a9c0e128bb6a677589fef6054c9..a7b7ae44274356702ffe34923d39405d23715f11 100644 --- a/lib/compiler/configurer.zig +++ b/lib/compiler/configurer.zig @@ -633,7 +633,8 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { var s: Serialize = .{ .wc = wc, .arena = arena }; try wc.path_deps.ensureTotalCapacityPrecise(gpa, graph.configure_dependencies.items.len); - for ( + // TODO remove this + if (false) for ( graph.configure_dependencies.items, wc.path_deps.addManyAsSliceAssumeCapacity(graph.configure_dependencies.items.len), ) |src, *dest| { @@ -661,7 +662,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { .dependency => |d| .init(try s.builderToPackage(d.dependency.builder)), }, }; - } + }; // Starting from all top-level steps in `b`, traverse the entire step graph // and add all step dependencies implied by module graphs. diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 0880d2abeaa9e4fa9580673b3a16f028c2b3d0f4..68f5cfc49af5e8e48724db42455115313108f9e1 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -1035,7 +1035,7 @@ pub const Manifest = struct { pub fn addPathPost(man: *Manifest, path: Path) !void { _ = man; _ = path; - @panic("TODO"); + std.log.err("TODO Build.Cache.addPathPost", .{}); } /// Like `addFilePost` but when the file contents have already been loaded from disk. diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig index 8e36cad974a76f24ef6e12f08802995c2b748544..72b876e577296b359ba86cee7df7d2fe65a8bb92 100644 --- a/lib/std/Build/Configuration.zig +++ b/lib/std/Build/Configuration.zig @@ -1881,7 +1881,7 @@ pub const PathDep = extern struct { _ = c; _ = arena; _ = path; - @panic("TODO"); + std.log.err("TODO Configuration.PathDep.toCachePath", .{}); } }; diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 05891c582e38646da978177e34a3e8b26ac7f0af..2e1e0769436f003a829af900cde18c3b9f440236 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -2,12 +2,17 @@ //! source lives here. These APIs are provided as-is and have absolutely no API //! guarantees whatsoever. +const builtin = @import("builtin"); + const std = @import("std.zig"); const assert = std.debug.assert; const mem = std.mem; const Allocator = std.mem.Allocator; const Io = std.Io; const Writer = std.Io.Writer; +const Cache = std.Build.Cache; +const fatal = std.process.fatal; +const Dir = std.Io.Dir; const tokenizer = @import("zig/tokenizer.zig"); @@ -47,6 +52,9 @@ pub const c_translation = struct { pub const helpers = @import("zig/c_translation/helpers.zig"); }; +pub const default_local_zig_cache_basename = ".zig-cache"; +pub const build_zig_basename = "build.zig"; + pub const SrcHasher = std.crypto.hash.Blake3; pub const SrcHash = [16]u8; @@ -70,7 +78,7 @@ pub const Color = enum { /// CLICOLOR_FORCE environment variables. Color is always disabled on WASI per /// https://github.com/WebAssembly/WASI/issues/162 pub fn settingFromEnvironment(environ_map: *const std.process.Environ.Map) Color { - return if (@import("builtin").os.tag == .wasi or EnvVar.NO_COLOR.isSet(environ_map)) + return if (builtin.os.tag == .wasi or EnvVar.NO_COLOR.isSet(environ_map)) .off else if (EnvVar.CLICOLOR_FORCE.isSet(environ_map)) .on @@ -163,8 +171,8 @@ pub const BinNameOptions = struct { os_tag: std.Target.Os.Tag, ofmt: std.Target.ObjectFormat, abi: std.Target.Abi, - output_mode: std.builtin.OutputMode, - link_mode: ?std.builtin.LinkMode = null, + output_mode: std.lang.OutputMode, + link_mode: ?std.lang.LinkMode = null, version: ?std.SemanticVersion = null, }; @@ -512,7 +520,7 @@ pub const FormatId = struct { pub fn format(ctx: FormatId, writer: *Writer) Writer.Error!void { const bytes = ctx.bytes; if (isValidId(bytes) and - (ctx.flags.allow_primitive or !std.zig.isPrimitive(bytes)) and + (ctx.flags.allow_primitive or !isPrimitive(bytes)) and (ctx.flags.allow_underscore or !isUnderscore(bytes))) { return writer.writeAll(bytes); @@ -592,7 +600,7 @@ pub fn isValidId(bytes: []const u8) bool { else => return false, } } - return std.zig.Token.getKeyword(bytes) == null; + return Token.getKeyword(bytes) == null; } test isValidId { @@ -658,7 +666,7 @@ pub fn readSourceFileToEndAlloc(gpa: Allocator, file_reader: *Io.File.Reader) ![ } pub fn printAstErrorsToStderr(gpa: Allocator, io: Io, tree: Ast, path: []const u8, color: Color) !void { - var wip_errors: std.zig.ErrorBundle.Wip = undefined; + var wip_errors: ErrorBundle.Wip = undefined; try wip_errors.init(gpa); defer wip_errors.deinit(); @@ -673,7 +681,7 @@ pub fn putAstErrorsIntoBundle( gpa: Allocator, tree: Ast, path: []const u8, - wip_errors: *std.zig.ErrorBundle.Wip, + wip_errors: *ErrorBundle.Wip, ) Allocator.Error!void { switch (tree.mode) { .zig => { @@ -692,7 +700,7 @@ pub fn putAstErrorsIntoBundle( } pub fn resolveTargetQueryOrFatal(io: Io, target_query: std.Target.Query) std.Target { - return std.zig.system.resolveTargetQuery(io, target_query) catch |err| + return system.resolveTargetQuery(io, target_query) catch |err| std.process.fatal("unable to resolve target: {t}", .{err}); } @@ -1242,6 +1250,365 @@ pub fn allocPrintCmd(gpa: Allocator, argv: []const []const u8, options: AllocPri return aw.toOwnedSlice(); } +/// Like `std.process.currentPathAlloc`, but also resolves the path with `Dir.path.resolve`. This +/// means the path has no repeated separators, no "." or ".." components, and no trailing separator. +/// On WASI, "" is returned instead of ".". +pub fn getResolvedCwd(io: Io, gpa: Allocator) std.process.CurrentPathAllocError![]u8 { + if (builtin.os.tag == .wasi) { + if (std.debug.runtime_safety) { + const cwd = try std.process.currentPathAlloc(io, gpa); + defer gpa.free(cwd); + assert(mem.eql(u8, cwd, ".")); + } + return ""; + } + const cwd = try std.process.currentPathAlloc(io, gpa); + defer gpa.free(cwd); + const resolved = try Dir.path.resolve(gpa, &.{cwd}); + assert(Dir.path.isAbsolute(resolved)); + return resolved; +} + +pub const Directories = struct { + /// The string returned by `introspect.getResolvedCwd`. This is typically an absolute path, + /// but on WASI is the empty string "" instead, because WASI does not have absolute paths. + cwd: []const u8, + /// The Zig 'lib' directory. + /// `zig_lib.path` is resolved (`resolvePath`) or `null` for cwd. + /// Guaranteed to be a different path from `global_cache` and `local_cache`. + zig_lib: Cache.Directory, + /// The global Zig cache directory. + /// `global_cache.path` is resolved (`resolvePath`) or `null` for cwd. + global_cache: Cache.Directory, + /// The local Zig cache directory. + /// `local_cache.path` is resolved (`resolvePath`) or `null` for cwd. + /// This may be the same as `global_cache`. + local_cache: Cache.Directory, + + pub fn deinit(dirs: *Directories, io: Io) void { + // The local and global caches could be the same. + const close_local = dirs.local_cache.handle.handle != dirs.global_cache.handle.handle; + + dirs.global_cache.handle.close(io); + if (close_local) dirs.local_cache.handle.close(io); + dirs.zig_lib.handle.close(io); + } + + /// Returns a `Directories` where `local_cache` is replaced with `global_cache`, intended for + /// use by sub-compilations (e.g. compiler_rt). Do not `deinit` the returned `Directories`; it + /// shares handles with `dirs`. + pub fn withoutLocalCache(dirs: Directories) Directories { + return .{ + .cwd = dirs.cwd, + .zig_lib = dirs.zig_lib, + .global_cache = dirs.global_cache, + .local_cache = dirs.global_cache, + }; + } + + const LocalCacheStrategy = union(enum) { + override: []const u8, + search, + global, + }; + + /// Uses `std.process.fatal` on error conditions. + pub fn init( + arena: Allocator, + io: Io, + override_zig_lib: ?[]const u8, + override_global_cache: ?[]const u8, + local_cache_strat: LocalCacheStrategy, + preopens: std.process.Preopens, + self_exe_path: switch (builtin.target.os.tag) { + .wasi => void, + else => []const u8, + }, + environ_map: *const std.process.Environ.Map, + cwd: []const u8, + ) Directories { + const wasi = builtin.target.os.tag == .wasi; + + const zig_lib: Cache.Directory = d: { + if (override_zig_lib) |path| break :d openUnresolved(arena, io, cwd, path, .@"zig lib"); + if (wasi) break :d getPreopen(preopens, "/lib"); + break :d findZigLibDirFromSelfExe(arena, io, cwd, self_exe_path) catch |err| { + fatal("unable to find zig installation directory {q}: {t}", .{ self_exe_path, err }); + }; + }; + + const global_cache: Cache.Directory = d: { + if (override_global_cache) |path| break :d openUnresolved(arena, io, cwd, path, .@"global cache"); + if (wasi) break :d getPreopen(preopens, "/cache"); + const path = resolveGlobalCacheDir(arena, environ_map) catch |err| { + fatal("unable to resolve zig cache directory: {t}", .{err}); + }; + break :d openUnresolved(arena, io, cwd, path, .@"global cache"); + }; + + const local_cache = getLocalCacheDirectory(arena, io, cwd, global_cache, local_cache_strat); + + if (std.mem.eql(u8, zig_lib.path orelse "", global_cache.path orelse "")) { + fatal("zig lib directory '{f}' cannot be equal to global cache directory '{f}'", .{ zig_lib, global_cache }); + } + if (std.mem.eql(u8, zig_lib.path orelse "", local_cache.path orelse "")) { + fatal("zig lib directory '{f}' cannot be equal to local cache directory '{f}'", .{ zig_lib, local_cache }); + } + + return .{ + .cwd = cwd, + .zig_lib = zig_lib, + .global_cache = global_cache, + .local_cache = local_cache, + }; + } + + fn getLocalCacheDirectory( + arena: Allocator, + io: Io, + cwd: []const u8, + global_cache: Cache.Directory, + local_cache_strat: LocalCacheStrategy, + ) Cache.Directory { + return switch (local_cache_strat) { + .override => |path| openUnresolved(arena, io, cwd, path, .@"local cache"), + .search => d: { + const maybe_path = resolveSuitableLocalCacheDir(arena, io, cwd) catch |err| + fatal("unable to resolve zig cache directory: {t}", .{err}); + const path = maybe_path orelse break :d global_cache; + break :d openUnresolved(arena, io, cwd, path, .@"local cache"); + }, + .global => global_cache, + }; + } + + fn getPreopen(preopens: std.process.Preopens, name: []const u8) Cache.Directory { + return .{ + .path = if (std.mem.eql(u8, name, ".")) null else name, + .handle = switch (preopens.get(name) orelse fatal("preopen not found: {q}", .{name})) { + .file => fatal("preopen {q} is not a directory", .{name}), + .dir => |d| d, + }, + }; + } + fn openUnresolved( + arena: Allocator, + io: Io, + cwd: []const u8, + unresolved_path: []const u8, + thing: enum { @"zig lib", @"global cache", @"local cache" }, + ) Cache.Directory { + const path = resolvePath(arena, cwd, &.{unresolved_path}) catch |err| { + fatal("unable to resolve {t} directory: {t}", .{ thing, err }); + }; + const nonempty_path = if (path.len == 0) "." else path; + const handle_or_err = switch (thing) { + .@"zig lib" => Dir.cwd().openDir(io, nonempty_path, .{}), + .@"global cache", .@"local cache" => Dir.cwd().createDirPathOpen(io, nonempty_path, .{}), + }; + return .{ + .path = if (path.len == 0) null else path, + .handle = handle_or_err catch |err| { + const extra_str: []const u8 = e: { + if (thing == .@"global cache") switch (err) { + error.AccessDenied, error.ReadOnlyFileSystem => break :e "\n" ++ + "If this location is not writable then consider specifying an alternative with " ++ + "the ZIG_GLOBAL_CACHE_DIR environment variable or the --global-cache-dir option.", + else => {}, + }; + break :e ""; + }; + fatal("unable to open {t} directory {q}: {t}{s}", .{ thing, nonempty_path, err, extra_str }); + }, + }; + } +}; + +/// Both the directory handle and the path are newly allocated resources which the caller now owns. +pub fn findZigLibDir(gpa: Allocator, io: Io) !Cache.Directory { + const cwd_path = try getResolvedCwd(io, gpa); + defer gpa.free(cwd_path); + const self_exe_path = try std.process.executablePathAlloc(io, gpa); + defer gpa.free(self_exe_path); + + return findZigLibDirFromSelfExe(gpa, io, cwd_path, self_exe_path); +} + +/// Both the directory handle and the path are newly allocated resources which the caller now owns. +pub fn findZigLibDirFromSelfExe( + allocator: Allocator, + io: Io, + /// The return value of `getResolvedCwd`. + /// Passed as an argument to avoid pointlessly repeating the call. + cwd_path: []const u8, + self_exe_path: []const u8, +) error{ OutOfMemory, FileNotFound }!Cache.Directory { + const cwd = Dir.cwd(); + var cur_path: []const u8 = self_exe_path; + while (Dir.path.dirname(cur_path)) |dirname| : (cur_path = dirname) { + var base_dir = cwd.openDir(io, dirname, .{}) catch continue; + defer base_dir.close(io); + + const sub_directory = testZigInstallPrefix(io, base_dir) orelse continue; + const p = try Dir.path.join(allocator, &.{ dirname, sub_directory.path.? }); + defer allocator.free(p); + + const resolved = try resolvePath(allocator, cwd_path, &.{p}); + return .{ + .handle = sub_directory.handle, + .path = if (resolved.len == 0) null else resolved, + }; + } + return error.FileNotFound; +} + +/// Returns the sub_path that worked, or `null` if none did. +/// The path of the returned Directory is relative to `base`. +/// The handle of the returned Directory is open. +fn testZigInstallPrefix(io: Io, base_dir: Dir) ?Cache.Directory { + const test_index_file = "std" ++ Dir.path.sep_str ++ "std.zig"; + + zig_dir: { + // Try lib/zig/std/std.zig + const lib_zig = "lib" ++ Dir.path.sep_str ++ "zig"; + var test_zig_dir = base_dir.openDir(io, lib_zig, .{}) catch break :zig_dir; + const file = test_zig_dir.openFile(io, test_index_file, .{}) catch { + test_zig_dir.close(io); + break :zig_dir; + }; + file.close(io); + return .{ .handle = test_zig_dir, .path = lib_zig }; + } + + // Try lib/std/std.zig + var test_zig_dir = base_dir.openDir(io, "lib", .{}) catch return null; + const file = test_zig_dir.openFile(io, test_index_file, .{}) catch { + test_zig_dir.close(io); + return null; + }; + file.close(io); + return .{ .handle = test_zig_dir, .path = "lib" }; +} + +pub fn resolveGlobalCacheDir(arena: Allocator, environ_map: *const std.process.Environ.Map) ![]const u8 { + if (EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map)) |value| return value; + + const app_name = "zig"; + + switch (builtin.os.tag) { + .wasi => @compileError("on WASI the global cache dir must be resolved with preopens"), + .windows => { + const local_app_data_dir = EnvVar.LOCALAPPDATA.get(environ_map) orelse + return error.AppDataDirUnavailable; + return Dir.path.join(arena, &.{ local_app_data_dir, app_name }); + }, + else => { + if (EnvVar.XDG_CACHE_HOME.get(environ_map)) |cache_root| { + if (cache_root.len > 0) { + return Dir.path.join(arena, &.{ cache_root, app_name }); + } + } + if (EnvVar.HOME.get(environ_map)) |home| { + if (home.len > 0) { + return Dir.path.join(arena, &.{ home, ".cache", app_name }); + } + } + return error.AppDataDirUnavailable; + }, + } +} + +/// Searches upwards from `cwd` for a directory containing a `build.zig` file. +/// If such a directory is found, returns the path to it joined to the `.zig_cache` name. +/// Otherwise, returns `null`, indicating no suitable local cache location. +pub fn resolveSuitableLocalCacheDir(arena: Allocator, io: Io, cwd: []const u8) Allocator.Error!?[]u8 { + var cur_dir = cwd; + while (true) { + const joined = try Dir.path.join(arena, &.{ cur_dir, build_zig_basename }); + if (Dir.cwd().access(io, joined, .{})) |_| { + return try Dir.path.join(arena, &.{ cur_dir, default_local_zig_cache_basename }); + } else |err| switch (err) { + error.FileNotFound => { + cur_dir = Dir.path.dirname(cur_dir) orelse return null; + continue; + }, + else => return null, + } + } +} + +/// Similar to `Dir.path.resolve`, but converts to a cwd-relative path, or, if that would +/// start with a relative up-dir (".."), an absolute path based on the cwd. Also, the cwd +/// returns the empty string ("") instead of ".". +pub fn resolvePath( + gpa: Allocator, + /// The return value of `getResolvedCwd`. + /// Passed as an argument to avoid pointlessly repeating the call. + cwd_resolved: []const u8, + paths: []const []const u8, +) Allocator.Error![]u8 { + if (builtin.target.os.tag == .wasi) { + assert(mem.eql(u8, cwd_resolved, "")); + const res = try Dir.path.resolve(gpa, paths); + if (mem.eql(u8, res, ".")) { + gpa.free(res); + return ""; + } + return res; + } + + // Heuristic for a fast path: if no component is absolute and ".." never appears, we just need to resolve `paths`. + for (paths) |p| { + if (Dir.path.isAbsolute(p)) break; // absolute path + if (mem.indexOf(u8, p, "..") != null) break; // may contain up-dir + } else { + // no absolute path, no "..". + const res = try Dir.path.resolve(gpa, paths); + if (mem.eql(u8, res, ".")) { + gpa.free(res); + return ""; + } + assert(!Dir.path.isAbsolute(res)); + assert(!isUpDir(res)); + return res; + } + + // The fast path failed; resolve the whole thing. + // Optimization: `paths` often has just one element. + const path_resolved = switch (paths.len) { + 0 => unreachable, + 1 => try Dir.path.resolve(gpa, &.{ cwd_resolved, paths[0] }), + else => r: { + const all_paths = try gpa.alloc([]const u8, paths.len + 1); + defer gpa.free(all_paths); + all_paths[0] = cwd_resolved; + @memcpy(all_paths[1..], paths); + break :r try Dir.path.resolve(gpa, all_paths); + }, + }; + errdefer gpa.free(path_resolved); + + assert(Dir.path.isAbsolute(path_resolved)); + assert(Dir.path.isAbsolute(cwd_resolved)); + + if (!std.mem.startsWith(u8, path_resolved, cwd_resolved)) return path_resolved; // not in cwd + if (path_resolved.len == cwd_resolved.len) { + // equal to cwd + gpa.free(path_resolved); + return ""; + } + if (path_resolved[cwd_resolved.len] != Dir.path.sep) return path_resolved; // not in cwd (last component differs) + + // in cwd; extract sub path + const sub_path = try gpa.dupe(u8, path_resolved[cwd_resolved.len + 1 ..]); + gpa.free(path_resolved); + return sub_path; +} + +pub fn isUpDir(p: []const u8) bool { + return mem.startsWith(u8, p, "..") and (p.len == 2 or p[2] == Dir.path.sep); +} + test { _ = Ast; _ = AstRlAnnotate; diff --git a/src/Compilation.zig b/src/Compilation.zig index 70ed4c7f9a55d8b1695520be78c264925f39928b..b130d4917c17a86a856f0ab02ee4552ddd179bda 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -17,7 +17,6 @@ const Value = @import("Value.zig"); const Type = @import("Type.zig"); const target_util = @import("target.zig"); const Package = @import("Package.zig"); -const introspect = @import("introspect.zig"); const link = @import("link.zig"); const tracy = @import("tracy.zig"); const trace = tracy.trace; @@ -190,7 +189,7 @@ parent_whole_cache: ?ParentWholeCache, /// Path to own executable for invoking `zig clang`. self_exe_path: ?[]const u8, /// Owned by the caller of `Compilation.create`. -dirs: Directories, +dirs: std.zig.Directories, libc_include_dir_list: []const []const u8, libc_framework_dir_list: []const []const u8, rc_includes: std.zig.RcIncludes, @@ -431,7 +430,7 @@ pub const Path = struct { } /// Given a `Path`, returns the directory handle and sub path to be used to open the path. - pub fn openInfo(p: Path, dirs: Directories) struct { Io.Dir, []const u8 } { + pub fn openInfo(p: Path, dirs: std.zig.Directories) struct { Io.Dir, []const u8 } { const dir = switch (p.root) { .none => { const cwd_sub_path = absToCwdRelative(p.sub_path, dirs.cwd); @@ -492,7 +491,7 @@ pub const Path = struct { /// From an unresolved path (which can be made of multiple not-yet-joined strings), construct a /// canonical `Path`. pub fn fromUnresolved(gpa: Allocator, dirs: Compilation.Directories, unresolved_parts: []const []const u8) Allocator.Error!Path { - const resolved = try introspect.resolvePath(gpa, dirs.cwd, unresolved_parts); + const resolved = try std.zig.resolvePath(gpa, dirs.cwd, unresolved_parts); errdefer gpa.free(resolved); // If, for instance, `dirs.local_cache.path` is within the lib dir, it must take priority, @@ -626,7 +625,7 @@ pub const Path = struct { }); } - pub fn toCachePath(p: Path, dirs: Directories) Cache.Path { + pub fn toCachePath(p: Path, dirs: std.zig.Directories) Cache.Path { const root_dir: Cache.Directory = switch (p.root) { .zig_lib => dirs.zig_lib, .global_cache => dirs.global_cache, @@ -649,7 +648,7 @@ pub const Path = struct { /// This should not be used for most of the compiler pipeline, but is useful when emitting /// paths from the compilation (e.g. in debug info), because they will not depend on the cwd. /// The returned path is owned by the caller and allocated into `gpa`. - pub fn toAbsolute(p: Path, dirs: Directories, gpa: Allocator) Allocator.Error![]u8 { + pub fn toAbsolute(p: Path, dirs: std.zig.Directories, gpa: Allocator) Allocator.Error![]u8 { const root_path: []const u8 = switch (p.root) { .zig_lib => dirs.zig_lib.path orelse "", .global_cache => dirs.global_cache.path orelse "", @@ -680,7 +679,7 @@ pub const Path = struct { /// Returns whether this `Path` is illegal to have as a user-imported `Zcu.File` (including /// as the root of a module). Such paths exist in directories which the Zig compiler treats /// specially, like 'global_cache/b/', which stores 'builtin.zig' files. - pub fn isIllegalZigImport(p: Path, gpa: Allocator, dirs: Directories) Allocator.Error!bool { + pub fn isIllegalZigImport(p: Path, gpa: Allocator, dirs: std.zig.Directories) Allocator.Error!bool { const zig_builtin_dir: Path = try .fromRoot(gpa, dirs, .global_cache, "b"); defer zig_builtin_dir.deinit(gpa); return switch (p.isNested(zig_builtin_dir)) { @@ -690,149 +689,6 @@ pub const Path = struct { } }; -pub const Directories = struct { - /// The string returned by `introspect.getResolvedCwd`. This is typically an absolute path, - /// but on WASI is the empty string "" instead, because WASI does not have absolute paths. - cwd: []const u8, - /// The Zig 'lib' directory. - /// `zig_lib.path` is resolved (`introspect.resolvePath`) or `null` for cwd. - /// Guaranteed to be a different path from `global_cache` and `local_cache`. - zig_lib: Cache.Directory, - /// The global Zig cache directory. - /// `global_cache.path` is resolved (`introspect.resolvePath`) or `null` for cwd. - global_cache: Cache.Directory, - /// The local Zig cache directory. - /// `local_cache.path` is resolved (`introspect.resolvePath`) or `null` for cwd. - /// This may be the same as `global_cache`. - local_cache: Cache.Directory, - - pub fn deinit(dirs: *Directories, io: Io) void { - // The local and global caches could be the same. - const close_local = dirs.local_cache.handle.handle != dirs.global_cache.handle.handle; - - dirs.global_cache.handle.close(io); - if (close_local) dirs.local_cache.handle.close(io); - dirs.zig_lib.handle.close(io); - } - - /// Returns a `Directories` where `local_cache` is replaced with `global_cache`, intended for - /// use by sub-compilations (e.g. compiler_rt). Do not `deinit` the returned `Directories`; it - /// shares handles with `dirs`. - pub fn withoutLocalCache(dirs: Directories) Directories { - return .{ - .cwd = dirs.cwd, - .zig_lib = dirs.zig_lib, - .global_cache = dirs.global_cache, - .local_cache = dirs.global_cache, - }; - } - - /// Uses `std.process.fatal` on error conditions. - pub fn init( - arena: Allocator, - io: Io, - override_zig_lib: ?[]const u8, - override_global_cache: ?[]const u8, - local_cache_strat: union(enum) { - override: []const u8, - search, - global, - }, - preopens: std.process.Preopens, - self_exe_path: switch (builtin.target.os.tag) { - .wasi => void, - else => []const u8, - }, - environ_map: *const std.process.Environ.Map, - cwd: []const u8, - ) Directories { - const wasi = builtin.target.os.tag == .wasi; - - const zig_lib: Cache.Directory = d: { - if (override_zig_lib) |path| break :d openUnresolved(arena, io, cwd, path, .@"zig lib"); - if (wasi) break :d getPreopen(preopens, "/lib"); - break :d introspect.findZigLibDirFromSelfExe(arena, io, cwd, self_exe_path) catch |err| { - fatal("unable to find zig installation directory '{s}': {t}", .{ self_exe_path, err }); - }; - }; - - const global_cache: Cache.Directory = d: { - if (override_global_cache) |path| break :d openUnresolved(arena, io, cwd, path, .@"global cache"); - if (wasi) break :d getPreopen(preopens, "/cache"); - const path = introspect.resolveGlobalCacheDir(arena, environ_map) catch |err| { - fatal("unable to resolve zig cache directory: {t}", .{err}); - }; - break :d openUnresolved(arena, io, cwd, path, .@"global cache"); - }; - - const local_cache: Cache.Directory = switch (local_cache_strat) { - .override => |path| openUnresolved(arena, io, cwd, path, .@"local cache"), - .search => d: { - const maybe_path = introspect.resolveSuitableLocalCacheDir(arena, io, cwd) catch |err| { - fatal("unable to resolve zig cache directory: {t}", .{err}); - }; - const path = maybe_path orelse break :d global_cache; - break :d openUnresolved(arena, io, cwd, path, .@"local cache"); - }, - .global => global_cache, - }; - - if (std.mem.eql(u8, zig_lib.path orelse "", global_cache.path orelse "")) { - fatal("zig lib directory '{f}' cannot be equal to global cache directory '{f}'", .{ zig_lib, global_cache }); - } - if (std.mem.eql(u8, zig_lib.path orelse "", local_cache.path orelse "")) { - fatal("zig lib directory '{f}' cannot be equal to local cache directory '{f}'", .{ zig_lib, local_cache }); - } - - return .{ - .cwd = cwd, - .zig_lib = zig_lib, - .global_cache = global_cache, - .local_cache = local_cache, - }; - } - fn getPreopen(preopens: std.process.Preopens, name: []const u8) Cache.Directory { - return .{ - .path = if (std.mem.eql(u8, name, ".")) null else name, - .handle = switch (preopens.get(name) orelse fatal("preopen not found: '{s}'", .{name})) { - .file => fatal("preopen {s} is not a directory", .{name}), - .dir => |d| d, - }, - }; - } - fn openUnresolved( - arena: Allocator, - io: Io, - cwd: []const u8, - unresolved_path: []const u8, - thing: enum { @"zig lib", @"global cache", @"local cache" }, - ) Cache.Directory { - const path = introspect.resolvePath(arena, cwd, &.{unresolved_path}) catch |err| { - fatal("unable to resolve {s} directory: {s}", .{ @tagName(thing), @errorName(err) }); - }; - const nonempty_path = if (path.len == 0) "." else path; - const handle_or_err = switch (thing) { - .@"zig lib" => Io.Dir.cwd().openDir(io, nonempty_path, .{}), - .@"global cache", .@"local cache" => Io.Dir.cwd().createDirPathOpen(io, nonempty_path, .{}), - }; - return .{ - .path = if (path.len == 0) null else path, - .handle = handle_or_err catch |err| { - const extra_str: []const u8 = e: { - if (thing == .@"global cache") switch (err) { - error.AccessDenied, error.ReadOnlyFileSystem => break :e "\n" ++ - "If this location is not writable then consider specifying an alternative with " ++ - "the ZIG_GLOBAL_CACHE_DIR environment variable or the --global-cache-dir option.", - else => {}, - }; - break :e ""; - }; - fatal("unable to open {s} directory '{s}': {s}{s}", .{ @tagName(thing), nonempty_path, @errorName(err), extra_str }); - }, - }; - } -}; - /// This small wrapper function just checks whether debug extensions are enabled before checking /// `comp.debug_incremental`. It is inline so that comptime-known `false` propagates to the caller, /// preventing debugging features from making it into release builds of the compiler. @@ -1549,7 +1405,7 @@ const CacheUse = union(CacheMode) { }; pub const CreateOptions = struct { - dirs: Directories, + dirs: std.zig.Directories, thread_limit: usize, self_exe_path: ?[]const u8 = null, diff --git a/src/Package/Module.zig b/src/Module.zig similarity index 97% rename from src/Package/Module.zig rename to src/Module.zig index 0c7e4166adf7d6c290cbb330bb12857c1cf21d90..02c65b09fcb13eb9ead45c4e9ce925d01b627e43 100644 --- a/src/Package/Module.zig +++ b/src/Module.zig @@ -1,4 +1,15 @@ //! Corresponds to something that Zig source code can `@import`. +const Module = @This(); + +const std = @import("std"); +const Allocator = std.mem.Allocator; +const Cache = std.Build.Cache; +const assert = std.debug.assert; + +const target_util = @import("../target.zig"); +const Builtin = @import("../Builtin.zig"); +const Compilation = @import("../Compilation.zig"); +const File = @import("../Zcu.zig").File; /// The root directory of the module. Only files inside this directory can be imported. root: Compilation.Path, @@ -37,11 +48,6 @@ no_builtin: bool, pub const Deps = std.array_hash_map.String(*Module); -pub const Tree = struct { - /// Each `Package` exposes a `Module` with build.zig as its root source file. - build_module_table: std.array_hash_map.Auto(MultiHashHexDigest, *Module), -}; - pub const CreateOptions = struct { paths: Paths, fully_qualified_name: []const u8, @@ -50,7 +56,7 @@ pub const CreateOptions = struct { inherited: Inherited, global: Compilation.Config, /// If this is null then `resolved_target` must be non-null. - parent: ?*Package.Module, + parent: ?*Module, pub const Paths = struct { root: Compilation.Path, @@ -107,7 +113,7 @@ pub const CreateError = error{ }; /// At least one of `parent` and `resolved_target` must be non-null. -pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { +pub fn create(arena: Allocator, options: CreateOptions) !*Module { if (options.inherited.sanitize_thread == true) assert(options.global.any_sanitize_thread); if (options.inherited.fuzz == true) assert(options.global.any_fuzz); if (options.inherited.single_threaded == false) assert(options.global.any_non_single_threaded); @@ -420,7 +426,7 @@ pub const LimitedOptions = struct { /// This one can only be used if the Module will only be used for AstGen and earlier in /// the pipeline. Illegal behavior occurs if a limited module touches Sema. -pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*Package.Module { +pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*Module { const mod = try gpa.create(Module); mod.* = .{ .root = options.root, @@ -515,15 +521,3 @@ pub fn getBuiltinOptions(m: Module, global: Compilation.Config) Builtin { .wasi_exec_model = global.wasi_exec_model, }; } - -const Module = @This(); -const Package = @import("../Package.zig"); -const std = @import("std"); -const Allocator = std.mem.Allocator; -const MultiHashHexDigest = Package.Manifest.MultiHashHexDigest; -const target_util = @import("../target.zig"); -const Cache = std.Build.Cache; -const Builtin = @import("../Builtin.zig"); -const assert = std.debug.assert; -const Compilation = @import("../Compilation.zig"); -const File = @import("../Zcu.zig").File; diff --git a/src/Zcu.zig b/src/Zcu.zig index e3a36fe31b987dce03f89d1f4694cf718d72d3dc..41758eaf3c3b2ae6ef9ce3adfef9ccb734bda5f4 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -34,7 +34,6 @@ const AstGen = std.zig.AstGen; const Sema = @import("Sema.zig"); const target_util = @import("target.zig"); const build_options = @import("build_options"); -const isUpDir = @import("introspect.zig").isUpDir; const InternPool = @import("InternPool.zig"); const Alignment = InternPool.Alignment; const AnalUnit = InternPool.AnalUnit; diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index e1cda016c163cdfde8cb693c5f143f2f86057980..f9aede4f72b4c0a4e32b06f02ac509ef62d30d09 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -23,7 +23,6 @@ const builtin = @import("builtin"); const dev = @import("../dev.zig"); const InternPool = @import("../InternPool.zig"); const AnalUnit = InternPool.AnalUnit; -const introspect = @import("../introspect.zig"); const Module = @import("../Package.zig").Module; const Sema = @import("../Sema.zig"); const target_util = @import("../target.zig"); diff --git a/src/dev.zig b/src/dev.zig index a6f97799157440329f3b248959e47f1585cfc1b3..8d3723ec101b8984b9d33381754ec3388374d3f6 100644 --- a/src/dev.zig +++ b/src/dev.zig @@ -112,7 +112,6 @@ pub const Env = enum { .translate_c_command, .fmt_command, .jit_command, - .fetch_command, .init_command, .targets_command, .version_command, @@ -252,7 +251,6 @@ pub const Feature = enum { translate_c_command, fmt_command, jit_command, - fetch_command, init_command, targets_command, version_command, diff --git a/src/introspect.zig b/src/introspect.zig deleted file mode 100644 index 13d00520936973fccc37ede11256842e0826f423..0000000000000000000000000000000000000000 --- a/src/introspect.zig +++ /dev/null @@ -1,220 +0,0 @@ -const builtin = @import("builtin"); - -const std = @import("std"); -const Io = std.Io; -const Dir = std.Io.Dir; -const mem = std.mem; -const Allocator = std.mem.Allocator; -const Cache = std.Build.Cache; -const assert = std.debug.assert; - -const build_options = @import("build_options"); - -const Compilation = @import("Compilation.zig"); -const Package = @import("Package.zig"); - -/// Returns the sub_path that worked, or `null` if none did. -/// The path of the returned Directory is relative to `base`. -/// The handle of the returned Directory is open. -fn testZigInstallPrefix(io: Io, base_dir: Io.Dir) ?Cache.Directory { - const test_index_file = "std" ++ Dir.path.sep_str ++ "std.zig"; - - zig_dir: { - // Try lib/zig/std/std.zig - const lib_zig = "lib" ++ Dir.path.sep_str ++ "zig"; - var test_zig_dir = base_dir.openDir(io, lib_zig, .{}) catch break :zig_dir; - const file = test_zig_dir.openFile(io, test_index_file, .{}) catch { - test_zig_dir.close(io); - break :zig_dir; - }; - file.close(io); - return .{ .handle = test_zig_dir, .path = lib_zig }; - } - - // Try lib/std/std.zig - var test_zig_dir = base_dir.openDir(io, "lib", .{}) catch return null; - const file = test_zig_dir.openFile(io, test_index_file, .{}) catch { - test_zig_dir.close(io); - return null; - }; - file.close(io); - return .{ .handle = test_zig_dir, .path = "lib" }; -} - -/// Both the directory handle and the path are newly allocated resources which the caller now owns. -pub fn findZigLibDir(gpa: Allocator, io: Io) !Cache.Directory { - const cwd_path = try getResolvedCwd(io, gpa); - defer gpa.free(cwd_path); - const self_exe_path = try std.process.executablePathAlloc(io, gpa); - defer gpa.free(self_exe_path); - - return findZigLibDirFromSelfExe(gpa, io, cwd_path, self_exe_path); -} - -/// Like `std.process.currentPathAlloc`, but also resolves the path with `Dir.path.resolve`. This -/// means the path has no repeated separators, no "." or ".." components, and no trailing separator. -/// On WASI, "" is returned instead of ".". -pub fn getResolvedCwd(io: Io, gpa: Allocator) std.process.CurrentPathAllocError![]u8 { - if (builtin.target.os.tag == .wasi) { - if (std.debug.runtime_safety) { - const cwd = try std.process.currentPathAlloc(io, gpa); - defer gpa.free(cwd); - assert(mem.eql(u8, cwd, ".")); - } - return ""; - } - const cwd = try std.process.currentPathAlloc(io, gpa); - defer gpa.free(cwd); - const resolved = try Dir.path.resolve(gpa, &.{cwd}); - assert(Dir.path.isAbsolute(resolved)); - return resolved; -} - -/// Both the directory handle and the path are newly allocated resources which the caller now owns. -pub fn findZigLibDirFromSelfExe( - allocator: Allocator, - io: Io, - /// The return value of `getResolvedCwd`. - /// Passed as an argument to avoid pointlessly repeating the call. - cwd_path: []const u8, - self_exe_path: []const u8, -) error{ OutOfMemory, FileNotFound }!Cache.Directory { - const cwd = Io.Dir.cwd(); - var cur_path: []const u8 = self_exe_path; - while (Dir.path.dirname(cur_path)) |dirname| : (cur_path = dirname) { - var base_dir = cwd.openDir(io, dirname, .{}) catch continue; - defer base_dir.close(io); - - const sub_directory = testZigInstallPrefix(io, base_dir) orelse continue; - const p = try Dir.path.join(allocator, &.{ dirname, sub_directory.path.? }); - defer allocator.free(p); - - const resolved = try resolvePath(allocator, cwd_path, &.{p}); - return .{ - .handle = sub_directory.handle, - .path = if (resolved.len == 0) null else resolved, - }; - } - return error.FileNotFound; -} - -pub fn resolveGlobalCacheDir(arena: Allocator, environ_map: *const std.process.Environ.Map) ![]const u8 { - if (std.zig.EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map)) |value| return value; - - const app_name = "zig"; - - switch (builtin.os.tag) { - .wasi => @compileError("on WASI the global cache dir must be resolved with preopens"), - .windows => { - const local_app_data_dir = std.zig.EnvVar.LOCALAPPDATA.get(environ_map) orelse - return error.AppDataDirUnavailable; - return Dir.path.join(arena, &.{ local_app_data_dir, app_name }); - }, - else => { - if (std.zig.EnvVar.XDG_CACHE_HOME.get(environ_map)) |cache_root| { - if (cache_root.len > 0) { - return Dir.path.join(arena, &.{ cache_root, app_name }); - } - } - if (std.zig.EnvVar.HOME.get(environ_map)) |home| { - if (home.len > 0) { - return Dir.path.join(arena, &.{ home, ".cache", app_name }); - } - } - return error.AppDataDirUnavailable; - }, - } -} - -/// Similar to `Dir.path.resolve`, but converts to a cwd-relative path, or, if that would -/// start with a relative up-dir (".."), an absolute path based on the cwd. Also, the cwd -/// returns the empty string ("") instead of ".". -pub fn resolvePath( - gpa: Allocator, - /// The return value of `getResolvedCwd`. - /// Passed as an argument to avoid pointlessly repeating the call. - cwd_resolved: []const u8, - paths: []const []const u8, -) Allocator.Error![]u8 { - if (builtin.target.os.tag == .wasi) { - assert(mem.eql(u8, cwd_resolved, "")); - const res = try Dir.path.resolve(gpa, paths); - if (mem.eql(u8, res, ".")) { - gpa.free(res); - return ""; - } - return res; - } - - // Heuristic for a fast path: if no component is absolute and ".." never appears, we just need to resolve `paths`. - for (paths) |p| { - if (Dir.path.isAbsolute(p)) break; // absolute path - if (mem.indexOf(u8, p, "..") != null) break; // may contain up-dir - } else { - // no absolute path, no "..". - const res = try Dir.path.resolve(gpa, paths); - if (mem.eql(u8, res, ".")) { - gpa.free(res); - return ""; - } - assert(!Dir.path.isAbsolute(res)); - assert(!isUpDir(res)); - return res; - } - - // The fast path failed; resolve the whole thing. - // Optimization: `paths` often has just one element. - const path_resolved = switch (paths.len) { - 0 => unreachable, - 1 => try Dir.path.resolve(gpa, &.{ cwd_resolved, paths[0] }), - else => r: { - const all_paths = try gpa.alloc([]const u8, paths.len + 1); - defer gpa.free(all_paths); - all_paths[0] = cwd_resolved; - @memcpy(all_paths[1..], paths); - break :r try Dir.path.resolve(gpa, all_paths); - }, - }; - errdefer gpa.free(path_resolved); - - assert(Dir.path.isAbsolute(path_resolved)); - assert(Dir.path.isAbsolute(cwd_resolved)); - - if (!std.mem.startsWith(u8, path_resolved, cwd_resolved)) return path_resolved; // not in cwd - if (path_resolved.len == cwd_resolved.len) { - // equal to cwd - gpa.free(path_resolved); - return ""; - } - if (path_resolved[cwd_resolved.len] != Dir.path.sep) return path_resolved; // not in cwd (last component differs) - - // in cwd; extract sub path - const sub_path = try gpa.dupe(u8, path_resolved[cwd_resolved.len + 1 ..]); - gpa.free(path_resolved); - return sub_path; -} - -pub fn isUpDir(p: []const u8) bool { - return mem.startsWith(u8, p, "..") and (p.len == 2 or p[2] == Dir.path.sep); -} - -pub const default_local_zig_cache_basename = ".zig-cache"; - -/// Searches upwards from `cwd` for a directory containing a `build.zig` file. -/// If such a directory is found, returns the path to it joined to the `.zig_cache` name. -/// Otherwise, returns `null`, indicating no suitable local cache location. -pub fn resolveSuitableLocalCacheDir(arena: Allocator, io: Io, cwd: []const u8) Allocator.Error!?[]u8 { - var cur_dir = cwd; - while (true) { - const joined = try Dir.path.join(arena, &.{ cur_dir, Package.build_zig_basename }); - if (Io.Dir.cwd().access(io, joined, .{})) |_| { - return try Dir.path.join(arena, &.{ cur_dir, default_local_zig_cache_basename }); - } else |err| switch (err) { - error.FileNotFound => { - cur_dir = Dir.path.dirname(cur_dir) orelse return null; - continue; - }, - else => return null, - } - } -} diff --git a/src/main.zig b/src/main.zig index 84b6553ff64750279537a242729f6ecf9c807639..4e255a4d496a9c7271c4d91b555b0e77647e5b81 100644 --- a/src/main.zig +++ b/src/main.zig @@ -21,13 +21,13 @@ const AstGen = std.zig.AstGen; const ZonGen = std.zig.ZonGen; const Server = std.zig.Server; const stringToEnum = std.meta.stringToEnum; +const allocPrint = std.fmt.allocPrint; pub const tracy = @import("tracy.zig"); const Compilation = @import("Compilation.zig"); const link = @import("link.zig"); const Package = @import("Package.zig"); const build_options = @import("build_options"); -const introspect = @import("introspect.zig"); const wasi_libc = @import("libs/wasi_libc.zig"); const target_util = @import("target.zig"); const crash_report = @import("crash_report.zig"); @@ -353,9 +353,15 @@ fn mainArgs( dev.check(.ar_command); return process.exit(try llvmArMain(arena, args)); }, - .build => { - dev.check(.build_command); - return cmdBuild(gpa, arena, io, cmd_args, environ_map); + .build, .fetch => { + return jitCmd(gpa, arena, io, args, environ_map, .{ + .cmd_name = "maker", + .root_src_path = "Maker.zig", + .prepend_zig_lib_dir_path = true, + .prepend_global_cache_path = true, + .prepend_zig_exe_path = true, + .prepend_seed = true, + }); }, .clang, .@"-cc1", .@"-cc1as" => { dev.check(.clang_command); @@ -385,7 +391,6 @@ fn mainArgs( .depend_on_aro = true, .prepend_zig_lib_dir_path = true, .server = use_server, - .color = Color.settingFromEnvironment(environ_map), }); }, .fmt => { @@ -396,25 +401,19 @@ fn mainArgs( return jitCmd(gpa, arena, io, cmd_args, environ_map, .{ .cmd_name = "objcopy", .root_src_path = "objcopy.zig", - .color = Color.settingFromEnvironment(environ_map), }); }, .objdump => { return jitCmd(gpa, arena, io, cmd_args, environ_map, .{ .cmd_name = "objdump", .root_src_path = "objdump.zig", - .color = Color.settingFromEnvironment(environ_map), }); }, - .fetch => { - return cmdFetch(gpa, arena, io, cmd_args, environ_map); - }, .libc => { return jitCmd(gpa, arena, io, cmd_args, environ_map, .{ .cmd_name = "libc", .root_src_path = "libc.zig", .prepend_zig_lib_dir_path = true, - .color = Color.settingFromEnvironment(environ_map), }); }, .std => { @@ -424,7 +423,6 @@ fn mainArgs( .prepend_zig_lib_dir_path = true, .prepend_zig_exe_path = true, .prepend_global_cache_path = true, - .color = Color.settingFromEnvironment(environ_map), }); }, .init => { @@ -461,7 +459,6 @@ fn mainArgs( return jitCmd(gpa, arena, io, cmd_args, environ_map, .{ .cmd_name = "reduce", .root_src_path = "reduce.zig", - .color = Color.settingFromEnvironment(environ_map), }); }, .zen => { @@ -2977,7 +2974,7 @@ fn buildOutputType( while (preprocessor_args_it.next()) |arg| { if (mem.eql(u8, arg, "-MD") or mem.eql(u8, arg, "-MMD") or mem.eql(u8, arg, "-MT")) { disable_c_depfile = true; - const cc_arg = try std.fmt.allocPrint(arena, "-Wp,{s},{s}", .{ arg, preprocessor_args_it.nextOrFatal() }); + const cc_arg = try allocPrint(arena, "-Wp,{s},{s}", .{ arg, preprocessor_args_it.nextOrFatal() }); try cc_argv.append(arena, cc_arg); } else { fatal("unsupported preprocessor arg: {s}", .{arg}); @@ -3222,7 +3219,7 @@ fn buildOutputType( else => process.executablePathAlloc(io, arena) catch |err| fatal("unable to find zig self exe path: {t}", .{err}), }; - const cwd_path = try introspect.getResolvedCwd(io, arena); + const cwd_path = try std.zig.getResolvedCwd(io, arena); // This `init` calls `fatal` on error. var dirs: Compilation.Directories = .init( @@ -3421,9 +3418,9 @@ fn buildOutputType( .yes_default_value => if (create_module.resolved_options.output_mode == .Lib and create_module.resolved_options.link_mode == .dynamic and target.ofmt == .elf) if (have_version) - try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ root_name, version.major }) + try allocPrint(arena, "lib{s}.so.{d}", .{ root_name, version.major }) else - try std.fmt.allocPrint(arena, "lib{s}.so", .{root_name}) + try allocPrint(arena, "lib{s}.so", .{root_name}) else null, }; @@ -3433,7 +3430,7 @@ fn buildOutputType( .yes_default_path => emit: { if (output_to_cache != null) break :emit .yes_cache; const name = switch (clang_preprocessor_mode) { - .pch => try std.fmt.allocPrint(arena, "{s}.pch", .{root_name}), + .pch => try allocPrint(arena, "{s}.pch", .{root_name}), else => try std.zig.binNameAlloc(arena, .{ .root_name = root_name, .cpu_arch = target.cpu.arch, @@ -3469,16 +3466,16 @@ fn buildOutputType( }, }; - const default_h_basename = try std.fmt.allocPrint(arena, "{s}.h", .{root_name}); + const default_h_basename = try allocPrint(arena, "{s}.h", .{root_name}); const emit_h_resolved = emit_h.resolve(io, default_h_basename, output_to_cache); - const default_asm_basename = try std.fmt.allocPrint(arena, "{s}.s", .{root_name}); + const default_asm_basename = try allocPrint(arena, "{s}.s", .{root_name}); const emit_asm_resolved = emit_asm.resolve(io, default_asm_basename, output_to_cache); - const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{s}.ll", .{root_name}); + const default_llvm_ir_basename = try allocPrint(arena, "{s}.ll", .{root_name}); const emit_llvm_ir_resolved = emit_llvm_ir.resolve(io, default_llvm_ir_basename, output_to_cache); - const default_llvm_bc_basename = try std.fmt.allocPrint(arena, "{s}.bc", .{root_name}); + const default_llvm_bc_basename = try allocPrint(arena, "{s}.bc", .{root_name}); const emit_llvm_bc_resolved = emit_llvm_bc.resolve(io, default_llvm_bc_basename, output_to_cache); const emit_docs_resolved = emit_docs.resolve(io, "docs", output_to_cache); @@ -3499,7 +3496,7 @@ fn buildOutputType( fatal("the argument -femit-implib is allowed only when building a Windows DLL", .{}); } } - const default_implib_basename = try std.fmt.allocPrint(arena, "{s}.lib", .{root_name}); + const default_implib_basename = try allocPrint(arena, "{s}.lib", .{root_name}); const emit_implib_resolved: Compilation.CreateOptions.Emit = switch (emit_implib) { .no => .no, .yes => emit_implib.resolve(io, default_implib_basename, output_to_cache), @@ -3528,7 +3525,7 @@ fn buildOutputType( // "-" is stdin. Dump it to a real file. const sep = fs.path.sep_str; - const dump_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-dump-stdin{s}", .{ + const dump_path = try allocPrint(arena, "tmp" ++ sep ++ "{x}-dump-stdin{s}", .{ randInt(io, u64), ext.canonicalName(target), }); try dirs.local_cache.handle.createDirPath(io, "tmp"); @@ -3557,7 +3554,7 @@ fn buildOutputType( const bin_digest: Cache.BinDigest = hasher.hasher.finalResult(); - const sub_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-stdin{s}", .{ + const sub_path = try allocPrint(arena, "tmp" ++ sep ++ "{x}-stdin{s}", .{ &bin_digest, ext.canonicalName(target), }); try dirs.local_cache.handle.rename(dump_path, dirs.local_cache.handle, sub_path, io); @@ -4586,7 +4583,7 @@ fn runOrTest( try argv.append(exe_path); if (arg_mode == .zig_test) { try argv.append( - try std.fmt.allocPrint(arena, "--seed=0x{x}", .{randInt(io, u32)}), + try allocPrint(arena, "--seed=0x{x}", .{randInt(io, u32)}), ); } } else { @@ -4794,7 +4791,7 @@ fn cmdTranslateC( assert(comp.c_source_files.len == 1); const c_source_file = comp.c_source_files[0]; - const translated_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{comp.root_name}); + const translated_basename = try allocPrint(arena, "{s}.zig", .{comp.root_name}); var man: Cache.Manifest = comp.obtainCObjectCacheManifest(comp.root_mod); man.want_shared_lock = false; @@ -4872,7 +4869,6 @@ pub fn translateC( .root_src_path = "translate-c/main.zig", .depend_on_aro = true, .capture = capture, - .color = Color.settingFromEnvironment(environ_map), }); } @@ -4912,7 +4908,7 @@ fn cmdInit(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! } } - const cwd_path = try introspect.getResolvedCwd(io, arena); + const cwd_path = try std.zig.getResolvedCwd(io, arena); const cwd_basename = fs.path.basename(cwd_path); const sanitized_root_name = try sanitizeExampleName(arena, cwd_basename); @@ -5027,1065 +5023,17 @@ test sanitizeExampleName { try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project")); } -fn cmdBuild( - gpa: Allocator, - arena: Allocator, - io: Io, - args: []const []const u8, - environ_map: *process.Environ.Map, -) !void { - var build_file: ?[]const u8 = null; - var override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map); - var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map); - var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(environ_map); - var override_pkg_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_PKG_DIR.get(environ_map); - var maker_optimize_mode: std.builtin.OptimizeMode = if (EnvVar.ZIG_DEBUG_CMD.isSet(environ_map)) - .Debug - else - .ReleaseSafe; - var configure_argv: std.ArrayList([]const u8) = .empty; - var make_argv: std.ArrayList([]const u8) = .empty; - var cached_passthru_configure: std.ArrayList(u32) = .empty; - var forks: std.ArrayList(Fork) = .empty; - var reference_trace: ?u32 = null; - var debug_compile_errors = false; - var verbose_link = (native_os != .wasi or builtin.link_libc) and - EnvVar.ZIG_VERBOSE_LINK.isSet(environ_map); - var verbose_cc = (native_os != .wasi or builtin.link_libc) and - EnvVar.ZIG_VERBOSE_CC.isSet(environ_map); - var verbose_air = false; - var verbose_intern_pool = false; - var verbose_generic_instances = false; - var verbose_llvm_ir: ?[]const u8 = null; - var verbose_llvm_bc: ?[]const u8 = null; - var verbose_llvm_cpu_features = false; - var fetch_only = false; - var fetch_mode: Package.Fetch.JobQueue.Mode = .needed; - var system_pkg_dir_path: ?[]const u8 = null; - var debug_target: ?[]const u8 = null; - var debug_libc_paths_file: ?[]const u8 = null; - var cache_poison: std.Build.Graph.CachePoison = .pure; - var print_configuration_path: bool = false; - - const self_exe_path = try process.executablePathAlloc(io, arena); - const default_seed = try std.fmt.allocPrint(arena, "0x{x}", .{randInt(io, u32)}); - - try configure_argv.ensureUnusedCapacity(arena, 16); - try make_argv.ensureUnusedCapacity(arena, 16); - try cached_passthru_configure.ensureUnusedCapacity(arena, 16); - - _ = configure_argv.addOneAssumeCapacity(); // configurer executable - _ = make_argv.addOneAssumeCapacity(); // maker executable - - make_argv.addManyAsArrayAssumeCapacity(2).* = .{ "--zig", self_exe_path }; - configure_argv.addManyAsArrayAssumeCapacity(2).* = .{ "--zig", self_exe_path }; - - make_argv.addManyAsArrayAssumeCapacity(2).* = .{ "--zig-lib-dir", undefined }; - const make_argv_index_zig_lib_dir = make_argv.items.len - 1; - - make_argv.addManyAsArrayAssumeCapacity(2).* = .{ "--build-root", undefined }; - const make_argv_index_build_root = make_argv.items.len - 1; - - make_argv.addManyAsArrayAssumeCapacity(2).* = .{ "--local-cache", undefined }; - const make_argv_index_cache_dir = make_argv.items.len - 1; - - make_argv.addManyAsArrayAssumeCapacity(2).* = .{ "--global-cache", undefined }; - const make_argv_index_global_cache_dir = make_argv.items.len - 1; - - make_argv.addManyAsArrayAssumeCapacity(2).* = .{ "--configuration", undefined }; - const argv_index_configuration_file = make_argv.items.len - 1; - - make_argv.addManyAsArrayAssumeCapacity(2).* = .{ "--seed", default_seed }; - const argv_index_seed = make_argv.items.len - 1; - - configure_argv.addManyAsArrayAssumeCapacity(2).* = .{ "--build-root", undefined }; - const conf_argv_index_build_root = configure_argv.items.len - 1; - - var color: Color = Color.settingFromEnvironment(environ_map); - var n_jobs: ?u32 = null; - - { - var i: usize = 0; - while (i < args.len) : (i += 1) { - const arg = args[i]; - 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); - continue; - } else if (mem.eql(u8, arg, "--system")) { - if (i + 1 >= args.len) fatal("expected argument after {q}", .{arg}); - i += 1; - system_pkg_dir_path = args[i]; - - try cached_passthru_configure.append(arena, @intCast(configure_argv.items.len)); - configure_argv.appendAssumeCapacity(arg); // Intentionally "--system" only; not the path. - continue; - } else if (mem.cutPrefix(u8, arg, "--color=")) |rest| { - color = stringToEnum(Color, rest) orelse - fatal("expected --color=[auto|on|off]; found {q}", .{arg}); - - try cached_passthru_configure.append(arena, @intCast(configure_argv.items.len)); - configure_argv.appendAssumeCapacity(arg); - continue; - } else if (mem.eql(u8, arg, "--cache-poison")) { - cache_poison = .poisoned; - configure_argv.appendAssumeCapacity("--cache-poison=poisoned"); - continue; - } else if (mem.cutPrefix(u8, arg, "--cache-poison=")) |rest| { - // Allow the configurer process to report parse failure. - if (stringToEnum(std.Build.Graph.CachePoison, rest)) |poison| { - cache_poison = poison; - } - configure_argv.appendAssumeCapacity(arg); - continue; - } else if (mem.eql(u8, arg, "--verbose")) { - // Intentionally is added both to make and configure but - // does not go into the cache hash. - configure_argv.appendAssumeCapacity(arg); - } else if (mem.eql(u8, arg, "--search-prefix")) { - if (i + 1 >= args.len) fatal("expected argument after: {s}", .{arg}); - i += 1; - // This argument is cache poisonous: it does not go into - // the cache and configurer must set the poison bit when - // choosing to observe it. - configure_argv.addManyAsArrayAssumeCapacity(2).* = .{ arg, args[i] }; - (try make_argv.addManyAsArray(arena, 2)).* = .{ arg, args[i] }; - continue; - } else if (mem.eql(u8, arg, "--build-file")) { - if (i + 1 >= args.len) fatal("expected argument after: {s}", .{arg}); - i += 1; - build_file = args[i]; - continue; - } else if (mem.eql(u8, arg, "--zig-lib-dir")) { - if (i + 1 >= args.len) fatal("expected argument after: {s}", .{arg}); - i += 1; - override_lib_dir = args[i]; - continue; - } else if (mem.eql(u8, arg, "--cache-dir")) { - if (i + 1 >= args.len) fatal("expected argument after: {s}", .{arg}); - i += 1; - override_local_cache_dir = args[i]; - continue; - } else if (mem.eql(u8, arg, "--pkg-dir")) { - if (i + 1 >= args.len) fatal("expected argument after: {s}", .{arg}); - i += 1; - override_pkg_dir = args[i]; - continue; - } else if (mem.eql(u8, arg, "--global-cache-dir")) { - if (i + 1 >= args.len) fatal("expected argument after: {s}", .{arg}); - i += 1; - override_global_cache_dir = args[i]; - continue; - } else if (mem.eql(u8, arg, "--print-configuration-path")) { - print_configuration_path = true; - continue; - } else if (mem.eql(u8, arg, "-freference-trace")) { - reference_trace = 256; - } else if (mem.eql(u8, arg, "--fetch")) { - fetch_only = true; - } else if (mem.cutPrefix(u8, arg, "--fetch=")) |sub_arg| { - fetch_only = true; - fetch_mode = stringToEnum(Package.Fetch.JobQueue.Mode, sub_arg) orelse - fatal("expected [needed|all] after \"--fetch=\", found: {s}", .{sub_arg}); - } else if (mem.cutPrefix(u8, arg, "--fork=")) |sub_arg| { - try forks.append(arena, .init(sub_arg)); - continue; - } else if (mem.eql(u8, arg, "--fork")) { - if (i + 1 >= args.len) fatal("expected argument after: {s}", .{arg}); - i += 1; - try forks.append(arena, .init(args[i])); - continue; - } else if (mem.cutPrefix(u8, arg, "-freference-trace=")) |num| { - 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")) { - reference_trace = null; - } else if (mem.cutPrefix(u8, arg, "--maker-opt=")) |rest| { - maker_optimize_mode = parseOptimizeMode(rest); - continue; - } else if (mem.eql(u8, arg, "--debug-log")) { - if (i + 1 >= args.len) fatal("expected argument after: {s}", .{arg}); - try make_argv.appendSlice(arena, args[i .. i + 2]); - i += 1; - try addDebugLog(arena, args[i]); - continue; - } else if (mem.eql(u8, arg, "--debug-compile-errors")) { - if (build_options.enable_debug_extensions) { - debug_compile_errors = true; - } else { - warn("Zig was compiled without debug extensions. --debug-compile-errors has no effect.", .{}); - } - } else if (mem.eql(u8, arg, "--debug-target")) { - if (i + 1 >= args.len) fatal("expected argument after {q}", .{arg}); - i += 1; - if (build_options.enable_debug_extensions) { - debug_target = args[i]; - } else { - warn("Zig was compiled without debug extensions. --debug-target has no effect.", .{}); - } - continue; - } else if (mem.eql(u8, arg, "--debug-libc")) { - if (i + 1 >= args.len) fatal("expected argument after {q}", .{arg}); - i += 1; - if (build_options.enable_debug_extensions) { - debug_libc_paths_file = args[i]; - } else { - warn("Zig was compiled without debug extensions. --debug-libc has no effect.", .{}); - } - continue; - } else if (mem.eql(u8, arg, "--verbose-link")) { - verbose_link = true; - } else if (mem.eql(u8, arg, "--verbose-cc")) { - verbose_cc = true; - } else if (mem.eql(u8, arg, "--verbose-air")) { - verbose_air = true; - } else if (mem.eql(u8, arg, "--verbose-intern-pool")) { - verbose_intern_pool = true; - } else if (mem.eql(u8, arg, "--verbose-generic-instances")) { - verbose_generic_instances = true; - } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) { - verbose_llvm_ir = "-"; - } else if (mem.cutPrefix(u8, arg, "--verbose-llvm-ir=")) |rest| { - verbose_llvm_ir = rest; - } else if (mem.cutPrefix(u8, arg, "--verbose-llvm-bc=")) |rest| { - verbose_llvm_bc = rest; - } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) { - verbose_llvm_cpu_features = true; - } else if (mem.cutPrefix(u8, arg, "-j")) |str| { - const num = std.fmt.parseUnsigned(u32, str, 10) catch |err| - fatal("unable to parse jobs count {s}: {t}", .{ str, err }); - if (num < 1) { - fatal("number of jobs must be at least 1", .{}); - } - n_jobs = num; - } else if (mem.eql(u8, arg, "--seed")) { - if (i + 1 >= args.len) fatal("expected argument after {q}", .{arg}); - i += 1; - make_argv.items[argv_index_seed] = args[i]; - continue; - } else if (mem.eql(u8, arg, "--")) { - try make_argv.appendSlice(arena, args[i..]); - break; - } - } - try make_argv.append(arena, arg); - } - } - - const root_prog_node = std.Progress.start(io, .{ - .disable_printing = (color == .off), - .root_name = "", - }); - defer root_prog_node.end(); - - process.raiseFileDescriptorLimit(); - - const cwd_path = introspect.getResolvedCwd(io, arena) catch |err| - fatal("failed to get current directory path: {t}", .{err}); - - const build_root = try findBuildRoot(arena, io, .{ - .cwd_path = cwd_path, - .build_file = build_file, - }); - - { - // This `init` calls `fatal` on error. - var dirs: Compilation.Directories = .init( - arena, - io, - override_lib_dir, - override_global_cache_dir, - .{ .override = path: { - if (override_local_cache_dir) |d| break :path d; - break :path try build_root.directory.join(arena, &.{introspect.default_local_zig_cache_basename}); - } }, - .empty, - self_exe_path, - environ_map, - cwd_path, - ); - defer dirs.deinit(io); - - const thread_limit = @min( - @max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1), - std.math.maxInt(Zcu.PerThread.IdBacking), - ); - try setThreadLimit(arena, thread_limit); - - // Cache lookup for configure options. If we get a match, we can skip - // execution of the configure script. If not, we get the file path to pass - // to the configure process. - var local_cache: Cache = .{ - .gpa = gpa, - .io = io, - .manifest_dir = try dirs.local_cache.handle.createDirPathOpen(io, "h", .{}), - .cwd = cwd_path, - }; - local_cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); - local_cache.addPrefix(dirs.zig_lib); - local_cache.addPrefix(dirs.local_cache); - local_cache.addPrefix(dirs.global_cache); - defer local_cache.manifest_dir.close(io); - - var config_man = local_cache.obtain(); - defer config_man.deinit(); - config_man.hash.addBytes(build_options.version); - - for (cached_passthru_configure.items) |i| - config_man.hash.addBytes(configure_argv.items[i]); - - // Prevents a `zig build` from getting a false positive cache hit following - // a `zig build --cache-poison=ignored`. - config_man.hash.add(cache_poison == .ignored); - - // Normally the build runner is compiled for the host target but here is - // some code to help when debugging edits to the build runner so that you - // can make sure it compiles successfully on other targets. - const resolved_target: Package.Module.ResolvedTarget = t: { - if (build_options.enable_debug_extensions) { - if (debug_target) |triple| { - const target_query = try std.Target.Query.parse(.{ - .arch_os_abi = triple, - }); - config_man.hash.addBytes(triple); - break :t .{ - .result = std.zig.resolveTargetQueryOrFatal(io, target_query), - .is_native_os = false, - .is_native_abi = false, - .is_explicit_dynamic_linker = false, - }; - } - } - break :t .{ - .result = std.zig.resolveTargetQueryOrFatal(io, .{}), - .is_native_os = true, - .is_native_abi = true, - .is_explicit_dynamic_linker = false, - }; - }; - - // Likewise, `--debug-libc` allows overriding the libc installation. - const libc_installation: ?*const LibCInstallation = lci: { - const paths_file = debug_libc_paths_file orelse break :lci null; - if (!build_options.enable_debug_extensions) unreachable; - const lci = try arena.create(LibCInstallation); - lci.* = try .parse(arena, io, paths_file, &resolved_target.result); - LibCInstallation.addToHash(lci, &config_man.hash, resolved_target.result.abi); - break :lci lci; - }; - - // Kick off an optimized compilation of the make runner. - var make_runner_task = if (print_configuration_path) undefined else io.async(compileMakeRunner, .{ gpa, arena, io, .{ - .dirs = .{ - .cwd = dirs.cwd, - .zig_lib = dirs.zig_lib, - .global_cache = dirs.global_cache, - .local_cache = dirs.global_cache, - }, - .environ_map = environ_map, - .parent_prog_node = root_prog_node, - .resolved_target = resolved_target, - .libc_installation = libc_installation, - .thread_limit = thread_limit, - .self_exe_path = self_exe_path, - .color = color, - .reference_trace = reference_trace, - .optimize_mode = maker_optimize_mode, - } }); - defer _ = if (!print_configuration_path) make_runner_task.cancel(io) catch {}; - - 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", - }; - - make_argv.items[make_argv_index_zig_lib_dir] = dirs.zig_lib.path orelse cwd_path; - make_argv.items[make_argv_index_build_root] = build_root.directory.path orelse cwd_path; - make_argv.items[make_argv_index_global_cache_dir] = dirs.global_cache.path orelse cwd_path; - make_argv.items[make_argv_index_cache_dir] = dirs.local_cache.path orelse cwd_path; - - configure_argv.items[conf_argv_index_build_root] = build_root.directory.path orelse cwd_path; - - // Dummy http client that is not actually used when fetch_command is unsupported. - // Prevents bootstrap from depending on a bunch of unnecessary stuff. - var http_client: if (dev.env.supports(.fetch_command)) std.http.Client else struct { - allocator: Allocator, - io: Io, - fn deinit(_: @This()) void {} - } = .{ .allocator = gpa, .io = io }; - defer http_client.deinit(); - - var unlazy_set: Package.Fetch.JobQueue.UnlazySet = .{}; - var fork_set: Package.Fetch.JobQueue.ForkSet = .{}; - - { - // Populate fork_set. - var group: Io.Group = .init; - defer group.cancel(io); - - for (forks.items) |*fork| - group.async(io, Fork.load, .{ io, gpa, fork, color }); - - try group.await(io); - - for (forks.items) |*fork| { - if (fork.failed) process.exit(1); - try fork_set.put(arena, .{ - .path = fork.path, - .manifest_ast = fork.manifest_ast, - .manifest = fork.manifest, - .uses = 0, - }, {}); - } - } - defer Fork.deinitList(forks.items); - - var file_system_inputs: std.ArrayList(u8) = .empty; - defer file_system_inputs.deinit(gpa); - - // This loop is re-evaluated when the build script exits with an indication that it - // could not continue due to missing lazy dependencies. - const configuration_path: Path, const poisoned: bool = cp: while (true) { - // We want to release all the locks before executing the child process, so we make a nice - // big block here to ensure the cleanup gets run when we extract out our argv. - { - const main_mod_paths: Package.Module.CreateOptions.Paths = .{ - .root = try .fromRoot(arena, dirs, .zig_lib, "compiler"), - .root_src_path = "configurer.zig", - }; - - const config = try Compilation.Config.resolve(.{ - .output_mode = .Exe, - .resolved_target = resolved_target, - .have_zcu = true, - .emit_bin = true, - .is_test = false, - }); - - const root_mod = try Package.Module.create(arena, .{ - .paths = main_mod_paths, - .fully_qualified_name = "root", - .cc_argv = &.{}, - .inherited = .{ - .resolved_target = resolved_target, - .single_threaded = true, - }, - .global = config, - .parent = null, - }); - - const build_mod = try Package.Module.create(arena, .{ - .paths = .{ - .root = try .fromUnresolved(arena, dirs, &.{build_root.directory.path orelse "."}), - .root_src_path = build_root.build_zig_basename, - }, - .fully_qualified_name = "root.@build", - .cc_argv = &.{}, - .inherited = .{}, - .global = config, - .parent = root_mod, - }); - - if (dev.env.supports(.fetch_command)) { - const fetch_prog_node = root_prog_node.start("Fetch Packages", 0); - defer fetch_prog_node.end(); - - // Reset fork match counts. - for (fork_set.keys()) |*fork| fork.uses = 0; - - var job_queue: Package.Fetch.JobQueue = .{ - .io = io, - .http_client = &http_client, - .global_cache = dirs.global_cache, - .local_storage = &.{ - .cache_root = .{ .root_dir = dirs.local_cache, .sub_path = "" }, - .pkg_root = pkg_root, - }, - .recursive = true, - .debug_hash = false, - .unlazy_set = unlazy_set, - .fork_set = fork_set, - .mode = fetch_mode, - .prog_node = fetch_prog_node, - .read_only = system_pkg_dir_path != null, - }; - defer job_queue.deinit(); - - if (system_pkg_dir_path == null) { - try http_client.initDefaultProxies(arena, environ_map); - } - - try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1); - try job_queue.table.ensureUnusedCapacity(gpa, 1); - - const phantom_package_root: Cache.Path = .{ .root_dir = build_root.directory }; - - var fetch: Package.Fetch = .{ - .arena = std.heap.ArenaAllocator.init(gpa), - .location = .{ .relative_path = phantom_package_root }, - .location_tok = 0, - .hash_tok = .none, - .name_tok = 0, - .lazy_status = .eager, - .remote_package_root = phantom_package_root, - .parent_package_root = phantom_package_root, - .parent_manifest_ast = null, - .prog_node = fetch_prog_node, - .job_queue = &job_queue, - .omit_missing_hash_error = true, - .allow_missing_paths_field = false, - .use_latest_commit = false, - - .package_root = undefined, - .error_bundle = undefined, - .manifest = undefined, - .manifest_ast = undefined, - .have_manifest = false, - .computed_hash = undefined, - .has_build_zig = true, - .oom_flag = false, - .latest_commit = null, - - .module = build_mod, - }; - - job_queue.all_fetches.appendAssumeCapacity(&fetch); - - job_queue.table.putAssumeCapacityNoClobber( - Package.Fetch.relativePathDigest(phantom_package_root, dirs.global_cache), - &fetch, - ); - - job_queue.group.async(io, Package.Fetch.workerRun, .{ &fetch, "root" }); - try job_queue.group.await(io); - - { - // Ensure that forks were actually used. This is done - // before printing manifest errors because using a fork can - // prevent them. - var any_unused = false; - for (fork_set.keys()) |*fork| { - if (fork.uses == 0) { - std.log.err("fork {f} matched no {s} packages", .{ - fork.path, fork.manifest.name, - }); - any_unused = true; - } else { - std.log.info("fork {f} matched {d} {s} packages", .{ - fork.path, fork.uses, fork.manifest.name, - }); - } - } - if (any_unused) process.exit(1); - } - - try job_queue.consolidateErrors(); - - if (fetch.error_bundle.root_list.items.len > 0) { - var errors = try fetch.error_bundle.toOwnedBundle(""); - errors.renderToStderr(io, .{}, color) catch {}; - process.exit(1); - } - - if (fetch_only) return cleanExit(io); - - var source_buf = std.array_list.Managed(u8).init(gpa); - defer source_buf.deinit(); - try job_queue.createDependenciesSource(&source_buf); - const deps_mod = try createDependenciesModule( - arena, - io, - source_buf.items, - root_mod, - dirs, - config, - ); - - { - // We need a Module for each package's build.zig. - const hashes = job_queue.table.keys(); - const fetches = job_queue.table.values(); - try deps_mod.deps.ensureUnusedCapacity(arena, @intCast(hashes.len)); - for (hashes, fetches) |*hash, f| { - if (f == &fetch) { - // The first one is a dummy package for the current project. - continue; - } - if (!f.has_build_zig) - continue; - const hash_slice = hash.toSlice(); - const mod_root_path = try f.package_root.toString(arena); - const m = try Package.Module.create(arena, .{ - .paths = .{ - .root = try .fromUnresolved(arena, dirs, &.{mod_root_path}), - .root_src_path = Package.build_zig_basename, - }, - .fully_qualified_name = try std.fmt.allocPrint( - arena, - "root.@dependencies.{s}", - .{hash_slice}, - ), - .cc_argv = &.{}, - .inherited = .{}, - .global = config, - .parent = root_mod, - }); - const hash_cloned = try arena.dupe(u8, hash_slice); - deps_mod.deps.putAssumeCapacityNoClobber(hash_cloned, m); - f.module = m; - } - - // Each build.zig module needs access to each of its - // dependencies' build.zig modules by name. - for (fetches) |f| { - const mod = f.module orelse continue; - if (!f.have_manifest) continue; - const man = &f.manifest; - const dep_names = man.dependencies.keys(); - try mod.deps.ensureUnusedCapacity(arena, @intCast(dep_names.len)); - for (dep_names, man.dependencies.values()) |name, dep| { - const dep_digest = Package.Fetch.depDigest( - f.package_root, - dirs.global_cache, - dep, - ) orelse continue; - const dep_mod = job_queue.table.get(dep_digest).?.module orelse continue; - const name_cloned = try arena.dupe(u8, name); - mod.deps.putAssumeCapacityNoClobber(name_cloned, dep_mod); - } - } - } - } else try createEmptyDependenciesModule( - arena, - io, - root_mod, - dirs, - config, - ); - - const compile_prog_node = root_prog_node.start("Compile Configure Script", 0); - defer compile_prog_node.end(); - - try root_mod.deps.put(arena, "@build", build_mod); - - file_system_inputs.clearRetainingCapacity(); - var create_diag: Compilation.CreateDiagnostic = undefined; - const comp = Compilation.create(gpa, arena, io, &create_diag, .{ - .libc_installation = libc_installation, - .dirs = dirs, - .root_name = "configure", - .config = config, - .root_mod = root_mod, - .main_mod = build_mod, - .emit_bin = .yes_cache, - .self_exe_path = self_exe_path, - .thread_limit = thread_limit, - .verbose_cc = verbose_cc, - .verbose_link = verbose_link, - .verbose_air = verbose_air, - .verbose_intern_pool = verbose_intern_pool, - .verbose_generic_instances = verbose_generic_instances, - .verbose_llvm_ir = verbose_llvm_ir, - .verbose_llvm_bc = verbose_llvm_bc, - .verbose_llvm_cpu_features = verbose_llvm_cpu_features, - .cache_mode = .whole, - .reference_trace = reference_trace, - .debug_compile_errors = debug_compile_errors, - .environ_map = environ_map, - .file_system_inputs = &file_system_inputs, - }) catch |err| switch (err) { - error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}), - else => |e| fatal("failed to create compilation: {t}", .{e}), - }; - defer comp.destroy(); - - updateModule(comp, color, compile_prog_node) catch |err| switch (err) { - error.CompileErrorsReported => process.exit(2), - else => |e| return e, - }; - - // Since incremental compilation isn't done yet, we use cache_mode = whole - // above, and thus the output file is already closed. - //try comp.makeBinFileExecutable(); - const hex_digest: []const u8 = &Cache.binToHex(comp.digest.?); - const exe_path: Path = .{ - .root_dir = dirs.local_cache, - .sub_path = try std.fmt.allocPrint(arena, "o/{s}/{s}", .{ hex_digest, comp.emit_bin.? }), - }; - _ = try config_man.addFilePath(exe_path, null); - configure_argv.items[0] = try exe_path.toString(arena); - - switch (cache_poison) { - .pure, .disallowed, .ignored => if (try config_man.hit()) { - const digest = config_man.final(); - break :cp .{ - .{ - .root_dir = dirs.local_cache, - .sub_path = try std.fmt.allocPrint(arena, "c/{s}", .{&digest}), - }, - false, - }; - }, - .poisoned => {}, // Don't bother checking for cache hit. - } - } - - if (!process.can_spawn) { - const cmd = try std.mem.join(arena, " ", configure_argv.items); - fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ native_os, cmd }); - } - - const rand_int = randInt(io, u64); - const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); - const config_tmp_path: Path = .{ - .root_dir = dirs.local_cache, - .sub_path = tmp_dir_sub_path, - }; - const config_tmp_file: Io.File = try config_tmp_path.root_dir.handle.createFile( - io, - config_tmp_path.sub_path, - .{ .read = true, .exclusive = true }, - ); - defer config_tmp_file.close(io); - - const term = term: { - const child_node = root_prog_node.start("Run Configure Script", 0); - defer child_node.end(); - var child = std.process.spawn(io, .{ - .argv = configure_argv.items, - .stdout = .{ .file = config_tmp_file }, - .progress_node = child_node, - }) catch |err| fatal("failed to spawn configure script {q}: {t}", .{ configure_argv.items[0], err }); - defer child.kill(io); - break :term child.wait(io) catch |err| - fatal("failed to wait configure script {q}: {t}", .{ configure_argv.items[0], err }); - }; - if (!term.success()) { - // Failure to produce the configuration file. - const cmd = try std.mem.join(arena, " ", configure_argv.items); - fatal("the following configure command {f}:\n{s}", .{ term, cmd }); - } - // Even though the file is designed to be sent directly to make - // runner, we must load it now because: - // * If it contains additional file dependencies, we need to - // add them to `config_man` before obtaining the final digest. - // * If it contains a set of lazy packages that need to be - // fetched, we need to fetch those now and re-run configure. - var configuration = std.Build.Configuration.loadFile(arena, io, config_tmp_file) catch |err| - fatal("failed to load configuration file {f}: {t}", .{ config_tmp_path, err }); - - if (configuration.unlazy_deps.len != 0) { - if (!dev.env.supports(.fetch_command)) process.exit(1); - var any_errors = false; - for (configuration.unlazy_deps) |hash_string| { - const hash = hash_string.slice(&configuration); - assert(hash.len != 0); - if (hash.len > Package.Hash.max_len) { - std.log.err("invalid digest (length {d} exceeds maximum): {q}", .{ hash.len, hash }); - any_errors = true; - continue; - } - try unlazy_set.put(arena, .fromSlice(hash), {}); - } - if (any_errors) process.exit(1); - if (system_pkg_dir_path) |p| { - // In this mode, the system needs to provide these packages; they - // cannot be fetched by Zig. - const s = fs.path.sep_str; - for (unlazy_set.keys()) |*hash| { - std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ p, hash.toSlice() }); - } - std.log.info("remote package fetching disabled due to --system mode", .{}); - std.log.info("dependencies might be avoidable depending on build configuration", .{}); - process.exit(1); - } - continue :cp; - } - - for (configuration.path_deps_base, configuration.path_deps_sub) |base, sub| { - const conf_path: std.Build.Configuration.Path = .{ .base = base, .sub = sub }; - try config_man.addPathPost(conf_path.toCachePath(&configuration, arena)); - } - - // We need to add to the configuration cache the source files of - // configurer itself, so that the maker process can watch the file system - // for those changes and restart itself. By doing this, we make it - // possible to bypass creating a Compilation for configurer on - // Configuration cache hit. - { - var it = mem.splitScalar(u8, file_system_inputs.items, 0); - while (it.next()) |input| { - _ = try config_man.addPrefixedPathPost(.{ - .prefix = input[0], - .sub_path = input[1..], - }); - } - } - - // If it is poisoned, there is no point in moving it to cached - // location. Just leave it in the tmp directory. - if (configuration.poisoned) { - break :cp .{ config_tmp_path, true }; - } else { - const digest = config_man.final(); - const final_path: Path = .{ - .root_dir = dirs.local_cache, - .sub_path = try std.fmt.allocPrint(arena, "c/{s}", .{&digest}), - }; - Io.Dir.rename( - config_tmp_path.root_dir.handle, - config_tmp_path.sub_path, - final_path.root_dir.handle, - final_path.sub_path, - io, - ) catch |err| retry: { - const e = switch (err) { - error.FileNotFound => e: { - const dir_path = final_path.dirname().?; - dir_path.root_dir.handle.createDirPath(io, dir_path.sub_path) catch |e| - fatal("failed to create directory {f}: {t}", .{ dir_path, e }); - if (Io.Dir.rename( - config_tmp_path.root_dir.handle, - config_tmp_path.sub_path, - final_path.root_dir.handle, - final_path.sub_path, - io, - )) |_| break :retry else |e| break :e e; - }, - else => |e| e, - }; - fatal("failed to rename configuration file from {f} into {f}: {t}", .{ - config_tmp_path, final_path, e, - }); - }; - config_man.writeManifest() catch |err| warn("failed to write cache manifest: {t}", .{err}); - break :cp .{ final_path, false }; - } - }; - - { - // Release all file system locks just before running the maker process. - var configuration_lock = if (!poisoned) config_man.toOwnedLock() else null; - defer if (configuration_lock) |*l| l.release(io); - - if (print_configuration_path) { - var stdout_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer); - stdout_writer.interface.print("{f}\n", .{configuration_path}) catch - fatal("failed printing cache file path: {t}", .{stdout_writer.err.?}); - stdout_writer.flush() catch |err| - fatal("failed printing cache file path: {t}", .{err}); - return cleanExit(io); - } - const make_runner = make_runner_task.await(io) catch |err| fatal("failed compiling maker: {t}", .{err}); - - make_argv.items[0] = try make_runner.exe_path.toString(arena); - make_argv.items[argv_index_configuration_file] = try configuration_path.toString(arena); - } - } - - if (!process.can_spawn) { - const cmd = try std.mem.join(arena, " ", make_argv.items); - fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ - native_os, cmd, - }); - } - - const term = term: { - _ = try io.lockStderr(&.{}, .no_color); - defer io.unlockStderr(); - var child = std.process.spawn(io, .{ - .argv = make_argv.items, - }) catch |err| fatal("failed spawning maker {s}: {t}", .{ make_argv.items[0], err }); - defer child.kill(io); - break :term child.wait(io) catch |err| - fatal("failed waiting on maker {s}: {t}", .{ make_argv.items[0], err }); - }; - if (term.success()) return cleanExit(io); - const cmd = try std.mem.join(arena, " ", make_argv.items); - fatal("the following maker command {f}:\n{s}", .{ term, cmd }); -} - -const MakeRunner = struct { - exe_path: Path, - - const Options = struct { - environ_map: *const process.Environ.Map, - dirs: Compilation.Directories, - parent_prog_node: std.Progress.Node, - resolved_target: Package.Module.ResolvedTarget, - libc_installation: ?*const LibCInstallation, - self_exe_path: []const u8, - thread_limit: usize, - color: Color, - reference_trace: ?u32, - optimize_mode: std.builtin.OptimizeMode, - }; -}; - -fn compileMakeRunner(gpa: Allocator, arena: Allocator, io: Io, options: MakeRunner.Options) !MakeRunner { - const compile_prog_node = options.parent_prog_node.start("Compiling Maker (first time setup)", 0); - defer compile_prog_node.end(); - - const strip = options.optimize_mode != .Debug; - - const main_mod_paths: Package.Module.CreateOptions.Paths = .{ - .root = try .fromRoot(arena, options.dirs, .zig_lib, "compiler"), - .root_src_path = "Maker.zig", - }; - - const config = try Compilation.Config.resolve(.{ - .output_mode = .Exe, - .root_strip = strip, - .root_optimize_mode = options.optimize_mode, - .resolved_target = options.resolved_target, - .have_zcu = true, - .emit_bin = true, - .is_test = false, - }); - - const root_mod = try Package.Module.create(arena, .{ - .paths = main_mod_paths, - .fully_qualified_name = "root", - .cc_argv = &.{}, - .inherited = .{ - .resolved_target = options.resolved_target, - .optimize_mode = options.optimize_mode, - .strip = strip, - }, - .global = config, - .parent = null, - }); - - var create_diag: Compilation.CreateDiagnostic = undefined; - const comp = Compilation.create(gpa, arena, io, &create_diag, .{ - .dirs = options.dirs, - .root_name = "maker", - .config = config, - .root_mod = root_mod, - .main_mod = root_mod, - .emit_bin = .yes_cache, - .self_exe_path = options.self_exe_path, - .thread_limit = options.thread_limit, - .cache_mode = .whole, - .environ_map = options.environ_map, - .reference_trace = options.reference_trace, - }) catch |err| switch (err) { - error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}), - error.Canceled => |e| return e, - else => |e| fatal("failed to create compilation: {t}", .{e}), - }; - defer comp.destroy(); - - try updateModule(comp, options.color, compile_prog_node); - - const exe_path: Path = .{ - .root_dir = options.dirs.global_cache, - .sub_path = try std.fmt.allocPrint(arena, "o/{s}/{s}", .{ - &Cache.binToHex(comp.digest.?), comp.emit_bin.?, - }), - }; - - return .{ - .exe_path = exe_path, - }; -} - -const Fork = struct { - path: Path, - manifest_ast: std.zig.Ast, - manifest: Package.Manifest, - error_bundle: std.zig.ErrorBundle.Wip, - failed: bool, - arena_allocator: std.heap.ArenaAllocator, - - fn init(cwd_relative_path: []const u8) Fork { - return .{ - .manifest_ast = undefined, - .manifest = undefined, - .error_bundle = undefined, - .arena_allocator = undefined, - .path = .{ - .root_dir = .cwd(), - .sub_path = cwd_relative_path, - }, - .failed = false, - }; - } - - fn load(io: Io, gpa: Allocator, fork: *Fork, color: Color) Io.Cancelable!void { - loadFallible(io, gpa, fork, color) catch |err| switch (err) { - error.Canceled => |e| return e, - error.AlreadyReported => fork.failed = true, - else => |e| { - std.log.err("failed to load fork at {f}: {t}", .{ fork.path, e }); - fork.failed = true; - }, - }; - } - - fn loadFallible(io: Io, gpa: Allocator, fork: *Fork, color: Color) !void { - fork.arena_allocator = .init(gpa); - const arena = fork.arena_allocator.allocator(); - - var error_bundle: std.zig.ErrorBundle.Wip = undefined; - try error_bundle.init(gpa); - defer error_bundle.deinit(); - - const manifest_path = try fork.path.join(arena, Package.Manifest.basename); - - Package.Manifest.load( - io, - arena, - manifest_path, - &fork.manifest_ast, - &error_bundle, - &fork.manifest, - true, - ) catch |err| switch (err) { - error.Canceled => |e| return e, - error.ErrorsBundled => { - assert(error_bundle.root_list.items.len > 0); - var errors = try error_bundle.toOwnedBundle(""); - errors.renderToStderr(io, .{}, color) catch {}; - return error.AlreadyReported; - }, - else => |e| { - std.log.err("failed to load package manifest {f}: {t}", .{ manifest_path, e }); - return error.AlreadyReported; - }, - }; - } - - fn deinitList(forks: []Fork) void { - for (forks) |*fork| fork.arena_allocator.deinit(); - } -}; - const JitCmdOptions = struct { cmd_name: []const u8, root_src_path: []const u8, prepend_zig_lib_dir_path: bool = false, prepend_global_cache_path: bool = false, prepend_zig_exe_path: bool = false, + prepend_seed: bool = false, depend_on_aro: bool = false, capture: ?*[]u8 = null, /// Send error bundles via std.zig.Server over stdout server: bool = false, - color: Color = .auto, }; fn jitCmd( @@ -6098,8 +5046,10 @@ fn jitCmd( ) !void { dev.check(.jit_command); + const color = Color.settingFromEnvironment(environ_map); + const root_prog_node = std.Progress.start(io, .{ - .disable_printing = (options.color == .off), + .disable_printing = (color == .off), }); defer root_prog_node.end(); @@ -6141,7 +5091,7 @@ fn jitCmdInner( const override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map); const override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map); - const cwd_path = try introspect.getResolvedCwd(io, arena); + const cwd_path = try std.zig.getResolvedCwd(io, arena); // This `init` calls `fatal` on error. var dirs: Compilation.Directories = .init( @@ -6158,7 +5108,7 @@ fn jitCmdInner( defer dirs.deinit(io); var child_argv: std.ArrayList([]const u8) = .empty; - try child_argv.ensureUnusedCapacity(arena, args.len + 4); + try child_argv.ensureUnusedCapacity(arena, args.len + 5); // We want to release all the locks before executing the child process, so we make a nice // big block here to ensure the cleanup gets run when we extract out our argv. @@ -6244,7 +5194,8 @@ fn jitCmdInner( process.exit(2); } } else { - updateModule(comp, options.color, root_prog_node) catch |err| switch (err) { + const color = Color.settingFromEnvironment(environ_map); + updateModule(comp, color, root_prog_node) catch |err| switch (err) { error.CompileErrorsReported => process.exit(2), else => |e| return e, }; @@ -6259,11 +5210,13 @@ fn jitCmdInner( } if (options.prepend_zig_lib_dir_path) - child_argv.appendAssumeCapacity(dirs.zig_lib.path.?); + child_argv.appendAssumeCapacity(try allocPrint(arena, "--zig-lib={s}", .{dirs.zig_lib.path.?})); if (options.prepend_zig_exe_path) - child_argv.appendAssumeCapacity(self_exe_path); + child_argv.appendAssumeCapacity(try allocPrint(arena, "--zig={s}", .{self_exe_path})); if (options.prepend_global_cache_path) - child_argv.appendAssumeCapacity(dirs.global_cache.path.?); + child_argv.appendAssumeCapacity(try allocPrint(arena, "--global-cache={s}", .{dirs.global_cache.path.?})); + if (options.prepend_seed) + child_argv.appendAssumeCapacity(try allocPrint(arena, "--seed=0x{x}", .{randInt(io, u32)})); child_argv.appendSliceAssumeCapacity(args); @@ -7199,567 +6152,6 @@ fn parseRcIncludes(arg: []const u8) std.zig.RcIncludes { fatal("unsupported rc includes type: {q}", .{arg}); } -const usage_fetch = - \\Usage: zig fetch [options] - \\Usage: zig fetch [options] - \\ - \\ Copy a package into the global cache and print its hash. - \\ must point to one of the following: - \\ - A git+http / git+https server for the package - \\ - A tarball file (with or without compression) containing - \\ package source - \\ - A git bundle file containing package source - \\ - \\Examples: - \\ - \\ zig fetch --save git+https://example.com/andrewrk/fun-example-tool.git - \\ zig fetch --save https://example.com/andrewrk/fun-example-tool/archive/refs/heads/master.tar.gz - \\ - \\Options: - \\ -h, --help Print this help and exit - \\ --global-cache-dir [path] Override path to global Zig cache directory - \\ --cache-dir [path] Override path to local cache directory - \\ --pkg-dir [path] Override path to local package directory - \\ --debug-hash Print verbose hash information to stdout - \\ --debug-log [scope] Enable printing debug/info log messages for scope - \\ --save Add the fetched package to build.zig.zon - \\ --save=[name] Add the fetched package to build.zig.zon as name - \\ --save-exact Add the fetched package to build.zig.zon, storing the URL verbatim - \\ --save-exact=[name] Add the fetched package to build.zig.zon as name, storing the URL verbatim - \\ -; - -fn cmdFetch( - gpa: Allocator, - arena: Allocator, - io: Io, - args: []const []const u8, - environ_map: *process.Environ.Map, -) !void { - dev.check(.fetch_command); - - const color: Color = Color.settingFromEnvironment(environ_map); - var opt_path_or_url: ?[]const u8 = null; - var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map); - var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(environ_map); - var override_pkg_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_PKG_DIR.get(environ_map); - var debug_hash: bool = false; - var save: union(enum) { - no, - yes: ?[]const u8, - exact: ?[]const u8, - } = .no; - - { - var i: usize = 0; - while (i < args.len) : (i += 1) { - const arg = args[i]; - if (mem.startsWith(u8, arg, "-")) { - if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - try Io.File.stdout().writeStreamingAll(io, usage_fetch); - return cleanExit(io); - } else if (mem.eql(u8, arg, "--global-cache-dir")) { - if (i + 1 >= args.len) fatal("expected argument after: {s}", .{arg}); - i += 1; - override_global_cache_dir = args[i]; - } else if (mem.eql(u8, arg, "--cache-dir")) { - if (i + 1 >= args.len) fatal("expected argument after: {s}", .{arg}); - i += 1; - override_local_cache_dir = args[i]; - } else if (mem.eql(u8, arg, "--pkg-dir")) { - if (i + 1 >= args.len) fatal("expected argument after: {s}", .{arg}); - i += 1; - override_pkg_dir = args[i]; - } else if (mem.eql(u8, arg, "--debug-hash")) { - debug_hash = true; - } else if (mem.eql(u8, arg, "--debug-log")) { - if (i + 1 >= args.len) fatal("expected argument after: {s}", .{arg}); - i += 1; - try addDebugLog(arena, args[i]); - } else if (mem.eql(u8, arg, "--save")) { - save = .{ .yes = null }; - } else if (mem.cutPrefix(u8, arg, "--save=")) |rest| { - save = .{ .yes = rest }; - } else if (mem.eql(u8, arg, "--save-exact")) { - save = .{ .exact = null }; - } else if (mem.cutPrefix(u8, arg, "--save-exact=")) |rest| { - save = .{ .exact = rest }; - } else { - fatal("unrecognized parameter: {q}", .{arg}); - } - } else if (opt_path_or_url != null) { - fatal("unexpected extra parameter: {q}", .{arg}); - } else { - opt_path_or_url = arg; - } - } - } - - const path_or_url = opt_path_or_url orelse fatal("missing url or path parameter", .{}); - - var http_client: std.http.Client = .{ .allocator = gpa, .io = io }; - defer http_client.deinit(); - - try http_client.initDefaultProxies(arena, environ_map); - - var root_prog_node = std.Progress.start(io, .{ - .root_name = "Fetch", - }); - defer root_prog_node.end(); - - var global_cache_directory: Directory = l: { - const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena, environ_map); - break :l .{ - .handle = try Io.Dir.cwd().createDirPathOpen(io, p, .{}), - .path = p, - }; - }; - defer global_cache_directory.handle.close(io); - - var local_storage: Package.Fetch.LocalStorage = undefined; - var build_root: BuildRoot = undefined; - var build_root_initialized = false; - defer if (build_root_initialized) build_root.deinit(io); - - const cwd_path = try introspect.getResolvedCwd(io, arena); - - const local_storage_ptr = switch (save) { - .no => null, - .yes, .exact => ls: { - build_root = try findBuildRoot(arena, io, .{ .cwd_path = cwd_path }); - build_root_initialized = true; - - local_storage = .{ - .cache_root = if (override_local_cache_dir) |p| .initCwd(p) else .{ - .root_dir = build_root.directory, - .sub_path = ".zig-cache", - }, - .pkg_root = if (override_pkg_dir) |p| .initCwd(p) else .{ - .root_dir = build_root.directory, - .sub_path = "zig-pkg", - }, - }; - - break :ls &local_storage; - }, - }; - - var job_queue: Package.Fetch.JobQueue = .{ - .io = io, - .http_client = &http_client, - .global_cache = global_cache_directory, - .local_storage = local_storage_ptr, - .recursive = false, - .read_only = false, - .debug_hash = debug_hash, - .mode = .all, - .prog_node = root_prog_node, - }; - defer job_queue.deinit(); - - var fetch: Package.Fetch = .{ - .arena = std.heap.ArenaAllocator.init(gpa), - .location = .{ .path_or_url = path_or_url }, - .location_tok = 0, - .hash_tok = .none, - .name_tok = 0, - .lazy_status = .eager, - .remote_package_root = undefined, - .parent_package_root = undefined, - .parent_manifest_ast = null, - .prog_node = root_prog_node, - .job_queue = &job_queue, - .omit_missing_hash_error = true, - .allow_missing_paths_field = false, - .use_latest_commit = true, - - .package_root = undefined, - .error_bundle = undefined, - .manifest = undefined, - .manifest_ast = undefined, - .have_manifest = false, - .computed_hash = undefined, - .has_build_zig = false, - .oom_flag = false, - .latest_commit = null, - - .module = null, - }; - defer fetch.deinit(); - - fetch.run() catch |err| switch (err) { - error.OutOfMemory, error.Canceled => |e| return e, - error.FetchFailed => {}, // error bundle checked below - }; - - try job_queue.group.await(io); - - if (fetch.error_bundle.root_list.items.len > 0) { - var errors = try fetch.error_bundle.toOwnedBundle(""); - errors.renderToStderr(io, .{}, color) catch {}; - process.exit(1); - } - - const package_hash = fetch.computedPackageHash(); - const package_hash_slice = package_hash.toSlice(); - - root_prog_node.end(); - root_prog_node = .{ .index = .none }; - - const name = switch (save) { - .no => { - var stdout = Io.File.stdout().writerStreaming(io, &stdout_buffer); - try stdout.interface.print("{s}\n", .{package_hash_slice}); - try stdout.interface.flush(); - return cleanExit(io); - }, - .yes, .exact => |name| name: { - if (name) |n| break :name n; - if (!fetch.have_manifest) - fatal("unable to determine name; fetched package has no build.zig.zon file", .{}); - break :name fetch.manifest.name; - }, - }; - - // The name to use in case the manifest file needs to be created now. - const init_root_name = fs.path.basename(build_root.directory.path orelse cwd_path); - var manifest, var ast = try loadManifest(gpa, arena, io, .{ - .root_name = try sanitizeExampleName(arena, init_root_name), - .dir = build_root.directory.handle, - .color = color, - }); - defer { - manifest.deinit(gpa); - ast.deinit(gpa); - } - - var fixups: Ast.Render.Fixups = .{}; - defer fixups.deinit(gpa); - - var saved_path_or_url = path_or_url; - - if (fetch.latest_commit) |latest_commit| resolved: { - const latest_commit_hex = try std.fmt.allocPrint(arena, "{f}", .{latest_commit}); - - var uri = try std.Uri.parse(path_or_url); - - if (uri.fragment) |fragment| { - const target_ref = try fragment.toRawMaybeAlloc(arena); - - // the refspec may already be fully resolved - if (std.mem.eql(u8, target_ref, latest_commit_hex)) break :resolved; - - std.log.info("resolved ref {q} to commit {s}", .{ target_ref, latest_commit_hex }); - - // include the original refspec in a query parameter, could be used to check for updates - uri.query = .{ .percent_encoded = try std.fmt.allocPrint(arena, "ref={f}", .{ - std.fmt.alt(fragment, .formatEscaped), - }) }; - } else { - std.log.info("resolved to commit {s}", .{latest_commit_hex}); - } - - // replace the refspec with the resolved commit SHA - uri.fragment = .{ .raw = latest_commit_hex }; - - switch (save) { - .yes => saved_path_or_url = try std.fmt.allocPrint(arena, "{f}", .{uri}), - .no, .exact => {}, // keep the original URL - } - } - - const new_node_init = try std.fmt.allocPrint(arena, - \\.{{ - \\ .url = "{f}", - \\ .hash = "{f}", - \\ }} - , .{ - std.zig.fmtString(saved_path_or_url), - std.zig.fmtString(package_hash_slice), - }); - - const new_node_text = try std.fmt.allocPrint(arena, ".{f} = {s},\n", .{ - std.zig.fmtIdPU(name), new_node_init, - }); - - const dependencies_init = try std.fmt.allocPrint(arena, ".{{\n {s} }}", .{ - new_node_text, - }); - - const dependencies_text = try std.fmt.allocPrint(arena, ".dependencies = {s},\n", .{ - dependencies_init, - }); - - if (manifest.dependencies.get(name)) |dep| { - if (dep.hash) |h| { - switch (dep.location) { - .url => |u| { - if (mem.eql(u8, h, package_hash_slice) and mem.eql(u8, u, saved_path_or_url)) { - std.log.info("existing dependency named {q} is up-to-date", .{name}); - process.exit(0); - } - }, - .path => {}, - } - } - - const location_replace = try std.fmt.allocPrint( - arena, - "\"{f}\"", - .{std.zig.fmtString(saved_path_or_url)}, - ); - const hash_replace = try std.fmt.allocPrint( - arena, - "\"{f}\"", - .{std.zig.fmtString(package_hash_slice)}, - ); - - warn("overwriting existing dependency named {q}", .{name}); - try fixups.replace_nodes_with_string.put(gpa, dep.location_node, location_replace); - if (dep.hash_node.unwrap()) |hash_node| { - try fixups.replace_nodes_with_string.put(gpa, hash_node, hash_replace); - } else { - // https://github.com/ziglang/zig/issues/21690 - } - } else if (manifest.dependencies.count() > 0) { - // Add fixup for adding another dependency. - const deps = manifest.dependencies.values(); - const last_dep_node = deps[deps.len - 1].node; - try fixups.append_string_after_node.put(gpa, last_dep_node, new_node_text); - } else if (manifest.dependencies_node.unwrap()) |dependencies_node| { - // Add fixup for replacing the entire dependencies struct. - try fixups.replace_nodes_with_string.put(gpa, dependencies_node, dependencies_init); - } else { - // Add fixup for adding dependencies struct. - try fixups.append_string_after_node.put(gpa, manifest.version_node, dependencies_text); - } - - var aw: Io.Writer.Allocating = .init(gpa); - defer aw.deinit(); - try ast.render(gpa, &aw.writer, fixups); - const rendered = aw.written(); - - build_root.directory.handle.writeFile(io, .{ .sub_path = Package.Manifest.basename, .data = rendered }) catch |err| { - fatal("unable to write {s} file: {t}", .{ Package.Manifest.basename, err }); - }; - - return cleanExit(io); -} - -fn createEmptyDependenciesModule( - arena: Allocator, - io: Io, - main_mod: *Package.Module, - dirs: Compilation.Directories, - global_options: Compilation.Config, -) !void { - var source = std.array_list.Managed(u8).init(arena); - try Package.Fetch.JobQueue.createEmptyDependenciesSource(&source); - _ = try createDependenciesModule( - arena, - io, - source.items, - main_mod, - dirs, - global_options, - ); -} - -/// Creates the dependencies.zig file and corresponding `Package.Module` for the -/// build runner to obtain via `@import("@dependencies")`. -fn createDependenciesModule( - arena: Allocator, - io: Io, - source: []const u8, - main_mod: *Package.Module, - dirs: Compilation.Directories, - global_options: Compilation.Config, -) !*Package.Module { - // Atomically create the file in a directory named after the hash of its contents. - const basename = "dependencies.zig"; - const rand_int = randInt(io, u64); - const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); - { - var tmp_dir = try dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{}); - defer tmp_dir.close(io); - try tmp_dir.writeFile(io, .{ .sub_path = basename, .data = source }); - } - const tmp_dir_path: Path = .{ - .root_dir = dirs.local_cache, - .sub_path = tmp_dir_sub_path, - }; - - var hh: Cache.HashHelper = .{}; - hh.addBytes(build_options.version); - hh.addBytes(source); - const hex_digest = hh.final(); - - const o_dir_path: Path = .{ - .root_dir = dirs.local_cache, - .sub_path = try arena.dupe(u8, "o" ++ fs.path.sep_str ++ hex_digest), - }; - try Package.Fetch.renameTmpIntoCache(io, tmp_dir_path, o_dir_path); - - const deps_mod = try Package.Module.create(arena, .{ - .paths = .{ - .root = try .fromRoot(arena, dirs, .local_cache, o_dir_path.sub_path), - .root_src_path = basename, - }, - .fully_qualified_name = "root.@dependencies", - .parent = main_mod, - .cc_argv = &.{}, - .inherited = .{}, - .global = global_options, - }); - try main_mod.deps.put(arena, "@dependencies", deps_mod); - return deps_mod; -} - -const BuildRoot = struct { - directory: Cache.Directory, - build_zig_basename: []const u8, - cleanup_build_dir: ?Io.Dir, - - fn deinit(br: *BuildRoot, io: Io) void { - if (br.cleanup_build_dir) |*dir| dir.close(io); - br.* = undefined; - } -}; - -const FindBuildRootOptions = struct { - build_file: ?[]const u8 = null, - cwd_path: ?[]const u8 = null, -}; - -fn findBuildRoot(arena: Allocator, io: Io, options: FindBuildRootOptions) !BuildRoot { - const cwd_path = options.cwd_path orelse try introspect.getResolvedCwd(io, arena); - const build_zig_basename = if (options.build_file) |bf| - fs.path.basename(bf) - else - Package.build_zig_basename; - - if (options.build_file) |bf| { - if (fs.path.dirname(bf)) |dirname| { - const dir = Io.Dir.cwd().openDir(io, dirname, .{}) catch |err| { - fatal("unable to open directory to build file from argument 'build-file', {q}: {t}", .{ dirname, err }); - }; - return .{ - .build_zig_basename = build_zig_basename, - .directory = .{ .path = dirname, .handle = dir }, - .cleanup_build_dir = dir, - }; - } - - return .{ - .build_zig_basename = build_zig_basename, - .directory = .{ .path = null, .handle = Io.Dir.cwd() }, - .cleanup_build_dir = null, - }; - } - // Search up parent directories until we find build.zig. - var dirname: []const u8 = cwd_path; - while (true) { - const joined_path = try fs.path.join(arena, &[_][]const u8{ dirname, build_zig_basename }); - if (Io.Dir.cwd().access(io, joined_path, .{})) |_| { - const dir = Io.Dir.cwd().openDir(io, dirname, .{}) catch |err| { - fatal("unable to open directory while searching for build.zig file, {q}: {t}", .{ dirname, err }); - }; - return .{ - .build_zig_basename = build_zig_basename, - .directory = .{ - .path = dirname, - .handle = dir, - }, - .cleanup_build_dir = dir, - }; - } else |err| switch (err) { - error.FileNotFound => { - dirname = fs.path.dirname(dirname) orelse { - std.log.info("initialize {s} template file with 'zig init'", .{ - Package.build_zig_basename, - }); - std.log.info("see 'zig --help' for more options", .{}); - fatal("no build.zig file found, in the current directory or any parent directories", .{}); - }; - continue; - }, - else => |e| return e, - } - } -} - -const LoadManifestOptions = struct { - root_name: []const u8, - dir: Io.Dir, - color: Color, -}; - -fn loadManifest( - gpa: Allocator, - arena: Allocator, - io: Io, - options: LoadManifestOptions, -) !struct { Package.Manifest, Ast } { - const rng: std.Random.IoSource = .{ .io = io }; - - const manifest_bytes = while (true) { - break options.dir.readFileAllocOptions( - io, - Package.Manifest.basename, - arena, - .limited(Package.Manifest.max_bytes), - .@"1", - 0, - ) catch |err| switch (err) { - error.FileNotFound => { - writeSimpleTemplateFile(io, Package.Manifest.basename, - \\.{{ - \\ .name = .{s}, - \\ .version = "{s}", - \\ .paths = .{{""}}, - \\ .fingerprint = 0x{x}, - \\}} - \\ - , .{ - options.root_name, - build_options.version, - Package.Fingerprint.generate(rng.interface(), options.root_name).int(), - }) catch |e| { - fatal("unable to write {s}: {t}", .{ Package.Manifest.basename, e }); - }; - continue; - }, - else => |e| fatal("unable to load {s}: {t}", .{ Package.Manifest.basename, e }), - }; - }; - var ast = try Ast.parse(gpa, manifest_bytes, .zon); - errdefer ast.deinit(gpa); - - if (ast.errors.len > 0) { - try std.zig.printAstErrorsToStderr(gpa, io, ast, Package.Manifest.basename, options.color); - process.exit(2); - } - - var manifest = try Package.Manifest.parse(gpa, &ast, rng.interface(), .{}); - errdefer manifest.deinit(gpa); - - if (manifest.errors.len > 0) { - var wip_errors: std.zig.ErrorBundle.Wip = undefined; - try wip_errors.init(gpa); - defer wip_errors.deinit(); - - const src_path = try wip_errors.addString(Package.Manifest.basename); - try manifest.copyErrorsIntoBundle(ast, src_path, &wip_errors); - - var error_bundle = try wip_errors.toOwnedBundle(""); - defer error_bundle.deinit(gpa); - error_bundle.renderToStderr(io, .{}, options.color) catch {}; - - process.exit(2); - } - return .{ manifest, ast }; -} - const Templates = struct { zig_lib_directory: Cache.Directory, dir: Io.Dir, @@ -7834,13 +6226,13 @@ fn writeSimpleTemplateFile(io: Io, file_name: []const u8, comptime fmt: []const } fn findTemplates(gpa: Allocator, arena: Allocator, io: Io) Templates { - const cwd_path = introspect.getResolvedCwd(io, arena) catch |err| { + const cwd_path = std.zig.getResolvedCwd(io, arena) catch |err| { fatal("unable to get cwd: {t}", .{err}); }; const self_exe_path = process.executablePathAlloc(io, arena) catch |err| { fatal("unable to find self exe path: {t}", .{err}); }; - var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, io, cwd_path, self_exe_path) catch |err| { + var zig_lib_directory = std.zig.findZigLibDirFromSelfExe(arena, io, cwd_path, self_exe_path) catch |err| { fatal("unable to find zig installation directory {q}: {t}", .{ self_exe_path, err }); }; diff --git a/src/print_env.zig b/src/print_env.zig index 9370006c72df78adc2725178d2644ca44fbbfcae..93e14781a184db46288b9c5e8c3d77e598820f2c 100644 --- a/src/print_env.zig +++ b/src/print_env.zig @@ -8,7 +8,6 @@ const fatal = std.process.fatal; const build_options = @import("build_options"); const Compilation = @import("Compilation.zig"); -const introspect = @import("introspect.zig"); pub fn cmdEnv( arena: Allocator, @@ -29,7 +28,7 @@ pub fn cmdEnv( }, }; - const cwd_path = try introspect.getResolvedCwd(io, arena); + const cwd_path = try std.zig.getResolvedCwd(io, arena); var dirs: Compilation.Directories = .init( arena, diff --git a/src/print_targets.zig b/src/print_targets.zig index c3a9ff44584ee2eb304cb63531fbda56ee1c98a1..702a684de3e829b8e7d475c265a5ed2e584caa1e 100644 --- a/src/print_targets.zig +++ b/src/print_targets.zig @@ -9,7 +9,6 @@ const Target = std.Target; const assert = std.debug.assert; const glibc = @import("libs/glibc.zig"); -const introspect = @import("introspect.zig"); const target = @import("target.zig"); pub fn cmdTargets( @@ -20,7 +19,7 @@ pub fn cmdTargets( native_target: *const Target, ) !void { _ = args; - var zig_lib_directory = introspect.findZigLibDir(allocator, io) catch |err| + var zig_lib_directory = std.zig.findZigLibDir(allocator, io) catch |err| fatal("unable to find zig installation directory: {t}", .{err}); defer zig_lib_directory.handle.close(io); defer allocator.free(zig_lib_directory.path.?); -- 2.54.0 From 27ae3b30add18fa1348c8e89cdace862e6b0bb09 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 22 Jun 2026 17:37:13 -0700 Subject: [PATCH 06/49] WIP: also move init subcommand to Maker process --- lib/compiler/Maker.zig | 274 +++++++++++++++++++++++++++++++++++++++-- src/main.zig | 256 +------------------------------------- 2 files changed, 266 insertions(+), 264 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 6fe474bee810bc95c0b6e6bcd6bfa063b60e8686..f7eb5b40d14cbfa6c02729435d7a0b27824d8bb8 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -170,8 +170,10 @@ pub fn main(init: process.Init.Minimal) !void { .random_seed = parseRandomSeed(seed_arg), }; - const cmd = stringToEnum(enum { fetch, build }, cmd_name) orelse fatal("bad command name: {q}", .{ cmd_name }); + const cmd = stringToEnum(enum { init, fetch, build }, cmd_name) orelse + fatal("bad command name: {q}", .{ cmd_name }); switch (cmd) { + .init => return cmdInit( gpa, &graph, args[arg_i..]), .fetch => return cmdFetch( gpa, &graph, args[arg_i..]), .build => {}, } @@ -978,7 +980,7 @@ pub fn main(init: process.Init.Minimal) !void { } const rand_int = randInt(io, u64); - const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); + const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); const config_tmp_path: Path = .{ .root_dir = dirs.local_cache, .sub_path = tmp_dir_sub_path, @@ -1033,7 +1035,7 @@ pub fn main(init: process.Init.Minimal) !void { if (system_pkg_dir_path) |p| { // In this mode, the system needs to provide these packages; they // cannot be fetched by Zig. - const s = fs.path.sep_str; + const s = Dir.path.sep_str; for (unlazy_set.keys()) |*hash| { std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ p, hash.toSlice() }); } @@ -1100,7 +1102,7 @@ pub fn main(init: process.Init.Minimal) !void { config_tmp_path, final_path, e, }); }; - config_man.writeManifest() catch |err| warn("failed to write cache manifest: {t}", .{err}); + config_man.writeManifest() catch |err| log.warn("failed to write cache manifest: {t}", .{err}); break :cp .{ final_path, false }; } }; @@ -1549,7 +1551,7 @@ fn cmdFetch( ast.deinit(gpa); } - var fixups: Ast.Render.Fixups = .{}; + var fixups: std.zig.Ast.Render.Fixups = .{}; defer fixups.deinit(gpa); var saved_path_or_url = path_or_url; @@ -1630,7 +1632,7 @@ fn cmdFetch( .{std.zig.fmtString(package_hash_slice)}, ); - warn("overwriting existing dependency named {q}", .{name}); + log.warn("overwriting existing dependency named {q}", .{name}); try fixups.replace_nodes_with_string.put(gpa, dep.location_node, location_replace); if (dep.hash_node.unwrap()) |hash_node| { try fixups.replace_nodes_with_string.put(gpa, hash_node, hash_replace); @@ -1692,10 +1694,129 @@ const usage_fetch = \\ ; -fn cmdBuild() !void { +const usage_init = + \\Usage: zig init + \\ + \\ Initializes a `zig build` project in the current working + \\ directory. + \\ + \\Options: + \\ -m, --minimal Use minimal init template + \\ -h, --help Print this help and exit + \\ + \\ +; +fn cmdInit( + gpa: Allocator, + graph: *Graph, + args: []const []const u8 +) !void { + const arena = graph.arena; + const io = graph.io; + + var template: enum { example, minimal } = .example; + { + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "-m") or mem.eql(u8, arg, "--minimal")) { + template = .minimal; + } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { + try Io.File.stdout().writeStreamingAll(io, usage_init); + return cleanExit(io); + } else { + fatal("unrecognized parameter: {q}", .{arg}); + } + } else { + fatal("unexpected extra parameter: {q}", .{arg}); + } + } + } + + const cwd_path = try std.zig.getResolvedCwd(io, arena); + const cwd_basename = Dir.path.basename(cwd_path); + const sanitized_root_name = try sanitizeExampleName(arena, cwd_basename); + + const rng: std.Random.IoSource = .{ .io = io }; + const fingerprint: Package.Fingerprint = .generate(rng.interface(), sanitized_root_name); + + switch (template) { + .example => { + var templates = findTemplates(gpa, arena, io); + defer templates.deinit(io); + + const s = Dir.path.sep_str; + const template_paths = [_][]const u8{ + Package.build_zig_basename, + Package.Manifest.basename, + "src" ++ s ++ "main.zig", + "src" ++ s ++ "root.zig", + }; + var ok_count: usize = 0; + + for (template_paths) |template_path| { + if (templates.write(arena, io, Io.Dir.cwd(), sanitized_root_name, template_path, fingerprint)) |_| { + std.log.info("created {s}", .{template_path}); + ok_count += 1; + } else |err| switch (err) { + error.PathAlreadyExists => std.log.info("preserving already existing file: {s}", .{ + template_path, + }), + else => std.log.err("unable to write {s}: {s}\n", .{ template_path, @errorName(err) }), + } + } + + if (ok_count == template_paths.len) { + std.log.info("see `zig build --help` for a menu of options", .{}); + } + return cleanExit(io); + }, + .minimal => { + writeSimpleTemplateFile(io, Package.Manifest.basename, + \\.{{ + \\ .name = .{s}, + \\ .version = "0.0.1", + \\ .minimum_zig_version = "{s}", + \\ .paths = .{{""}}, + \\ .fingerprint = 0x{x}, + \\}} + \\ + , .{ + sanitized_root_name, + builtin.zig_version_string, + fingerprint.int(), + }) catch |err| switch (err) { + else => fatal("failed to create {q}: {t}", .{ Package.Manifest.basename, err }), + error.PathAlreadyExists => fatal("refusing to overwrite {q}", .{Package.Manifest.basename}), + }; + writeSimpleTemplateFile(io, Package.build_zig_basename, + \\const std = @import("std"); + \\ + \\pub fn build(b: *std.Build) void {{ + \\ _ = b; // stub + \\}} + \\ + , .{}) catch |err| switch (err) { + else => fatal("failed to create {q}: {t}", .{ Package.build_zig_basename, err }), + // `build.zig` already existing is okay: the user has just used `zig init` to set up + // their `build.zig.zon` *after* writing their `build.zig`. So this one isn't fatal. + error.PathAlreadyExists => { + std.log.info("successfully populated {q}, preserving existing {q}", .{ + Package.Manifest.basename, Package.build_zig_basename, + }); + return cleanExit(io); + }, + }; + std.log.info("successfully populated {q} and {q}", .{ Package.Manifest.basename, Package.build_zig_basename }); + return cleanExit(io); + }, + } } + + fn markFailedStepsDirty(maker: *Maker) void { const all_steps = maker.step_stack.keys(); @@ -3234,7 +3355,7 @@ fn loadManifest( \\ , .{ options.root_name, - build_options.version, + builtin.zig_version_string, Package.Fingerprint.generate(rng.interface(), options.root_name).int(), }) catch |e| { fatal("unable to write {s}: {t}", .{ Package.Manifest.basename, e }); @@ -3244,7 +3365,7 @@ fn loadManifest( else => |e| fatal("unable to load {s}: {t}", .{ Package.Manifest.basename, e }), }; }; - var ast = try Ast.parse(gpa, manifest_bytes, .zon); + var ast = try std.zig.Ast.parse(gpa, manifest_bytes, .zon); errdefer ast.deinit(gpa); if (ast.errors.len > 0) { @@ -3272,3 +3393,138 @@ fn loadManifest( return .{ manifest, ast }; } +fn sanitizeExampleName(arena: Allocator, bytes: []const u8) error{OutOfMemory}![]const u8 { + var result: std.ArrayList(u8) = .empty; + for (bytes, 0..) |byte, i| switch (byte) { + '0'...'9' => { + if (i == 0) try result.append(arena, '_'); + try result.append(arena, byte); + }, + '_', 'a'...'z', 'A'...'Z' => try result.append(arena, byte), + '-', '.', ' ' => try result.append(arena, '_'), + else => continue, + }; + if (!std.zig.isValidId(result.items)) return "foo"; + if (result.items.len > Package.Manifest.max_name_len) + result.shrinkRetainingCapacity(Package.Manifest.max_name_len); + + return result.toOwnedSlice(arena); +} + +test sanitizeExampleName { + var arena_instance = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena_instance.deinit(); + const arena = arena_instance.allocator(); + + try std.testing.expectEqualStrings("foo_bar", try sanitizeExampleName(arena, "foo bar+")); + try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "")); + try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "!")); + try std.testing.expectEqualStrings("a", try sanitizeExampleName(arena, "!a")); + try std.testing.expectEqualStrings("a_b", try sanitizeExampleName(arena, "a.b!")); + try std.testing.expectEqualStrings("_01234", try sanitizeExampleName(arena, "01234")); + try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "error")); + try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "test")); + try std.testing.expectEqualStrings("tests", try sanitizeExampleName(arena, "tests")); + try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project")); +} + +const Templates = struct { + zig_lib_directory: Cache.Directory, + dir: Io.Dir, + buffer: std.array_list.Managed(u8), + + fn deinit(templates: *Templates, io: Io) void { + templates.zig_lib_directory.handle.close(io); + templates.dir.close(io); + templates.buffer.deinit(); + templates.* = undefined; + } + + fn write( + templates: *Templates, + arena: Allocator, + io: Io, + out_dir: Io.Dir, + root_name: []const u8, + template_path: []const u8, + fingerprint: Package.Fingerprint, + ) !void { + if (Dir.path.dirname(template_path)) |dirname| { + out_dir.createDirPath(io, dirname) catch |err| { + fatal("unable to make path {q}: {t}", .{ dirname, err }); + }; + } + + const max_bytes = 10 * 1024 * 1024; + const contents = templates.dir.readFileAlloc(io, template_path, arena, .limited(max_bytes)) catch |err| { + fatal("unable to read template file {q}: {t}", .{ template_path, err }); + }; + templates.buffer.clearRetainingCapacity(); + try templates.buffer.ensureUnusedCapacity(contents.len); + var i: usize = 0; + while (i < contents.len) { + if (contents[i] == '_' or contents[i] == '.') { + // Both '_' and '.' are allowed because depending on the context + // one prefix will be valid, while the other might not. + if (std.mem.startsWith(u8, contents[i + 1 ..], "NAME")) { + try templates.buffer.appendSlice(root_name); + i += "_NAME".len; + continue; + } else if (std.mem.startsWith(u8, contents[i + 1 ..], "FINGERPRINT")) { + try templates.buffer.print("0x{x}", .{fingerprint.int()}); + i += "_FINGERPRINT".len; + continue; + } else if (std.mem.startsWith(u8, contents[i + 1 ..], "ZIGVER")) { + try templates.buffer.appendSlice(builtin.zig_version_string); + i += "_ZIGVER".len; + continue; + } + } + + try templates.buffer.append(contents[i]); + i += 1; + } + + return out_dir.writeFile(io, .{ + .sub_path = template_path, + .data = templates.buffer.items, + .flags = .{ .exclusive = true }, + }); + } +}; +fn writeSimpleTemplateFile(io: Io, file_name: []const u8, comptime format: []const u8, args: anytype) !void { + const f = try Io.Dir.cwd().createFile(io, file_name, .{ .exclusive = true }); + defer f.close(io); + var buf: [4096]u8 = undefined; + var fw = f.writer(io, &buf); + try fw.interface.print(format, args); + try fw.interface.flush(); +} + +fn findTemplates(gpa: Allocator, arena: Allocator, io: Io) Templates { + const cwd_path = std.zig.getResolvedCwd(io, arena) catch |err| { + fatal("unable to get cwd: {t}", .{err}); + }; + const self_exe_path = process.executablePathAlloc(io, arena) catch |err| { + fatal("unable to find self exe path: {t}", .{err}); + }; + var zig_lib_directory = std.zig.findZigLibDirFromSelfExe(arena, io, cwd_path, self_exe_path) catch |err| { + fatal("unable to find zig installation directory {q}: {t}", .{ self_exe_path, err }); + }; + + const s = Dir.path.sep_str; + const template_sub_path = "init"; + const template_dir = zig_lib_directory.handle.openDir(io, template_sub_path, .{}) catch |err| { + const path = zig_lib_directory.path orelse "."; + fatal("unable to open zig project template directory '{s}{s}{s}': {t}", .{ + path, s, template_sub_path, err, + }); + }; + + return .{ + .zig_lib_directory = zig_lib_directory, + .dir = template_dir, + .buffer = std.array_list.Managed(u8).init(gpa), + }; +} + diff --git a/src/main.zig b/src/main.zig index 4e255a4d496a9c7271c4d91b555b0e77647e5b81..9d088946bcfeb147da456cb983953935a1c3c0aa 100644 --- a/src/main.zig +++ b/src/main.zig @@ -353,7 +353,7 @@ fn mainArgs( dev.check(.ar_command); return process.exit(try llvmArMain(arena, args)); }, - .build, .fetch => { + .build, .fetch, .init => { return jitCmd(gpa, arena, io, args, environ_map, .{ .cmd_name = "maker", .root_src_path = "Maker.zig", @@ -425,9 +425,6 @@ fn mainArgs( .prepend_global_cache_path = true, }); }, - .init => { - return cmdInit(gpa, arena, io, cmd_args); - }, .targets => { dev.check(.targets_command); const host = std.zig.resolveTargetQueryOrFatal(io, .{}); @@ -4872,157 +4869,6 @@ pub fn translateC( }); } -const usage_init = - \\Usage: zig init - \\ - \\ Initializes a `zig build` project in the current working - \\ directory. - \\ - \\Options: - \\ -m, --minimal Use minimal init template - \\ -h, --help Print this help and exit - \\ - \\ -; - -fn cmdInit(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !void { - dev.check(.init_command); - - var template: enum { example, minimal } = .example; - { - var i: usize = 0; - while (i < args.len) : (i += 1) { - const arg = args[i]; - if (mem.startsWith(u8, arg, "-")) { - if (mem.eql(u8, arg, "-m") or mem.eql(u8, arg, "--minimal")) { - template = .minimal; - } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - try Io.File.stdout().writeStreamingAll(io, usage_init); - return cleanExit(io); - } else { - fatal("unrecognized parameter: {q}", .{arg}); - } - } else { - fatal("unexpected extra parameter: {q}", .{arg}); - } - } - } - - const cwd_path = try std.zig.getResolvedCwd(io, arena); - const cwd_basename = fs.path.basename(cwd_path); - const sanitized_root_name = try sanitizeExampleName(arena, cwd_basename); - - const rng: std.Random.IoSource = .{ .io = io }; - const fingerprint: Package.Fingerprint = .generate(rng.interface(), sanitized_root_name); - - switch (template) { - .example => { - var templates = findTemplates(gpa, arena, io); - defer templates.deinit(io); - - const s = fs.path.sep_str; - const template_paths = [_][]const u8{ - Package.build_zig_basename, - Package.Manifest.basename, - "src" ++ s ++ "main.zig", - "src" ++ s ++ "root.zig", - }; - var ok_count: usize = 0; - - for (template_paths) |template_path| { - if (templates.write(arena, io, Io.Dir.cwd(), sanitized_root_name, template_path, fingerprint)) |_| { - std.log.info("created {s}", .{template_path}); - ok_count += 1; - } else |err| switch (err) { - error.PathAlreadyExists => std.log.info("preserving already existing file: {s}", .{ - template_path, - }), - else => std.log.err("unable to write {s}: {s}\n", .{ template_path, @errorName(err) }), - } - } - - if (ok_count == template_paths.len) { - std.log.info("see `zig build --help` for a menu of options", .{}); - } - return cleanExit(io); - }, - .minimal => { - writeSimpleTemplateFile(io, Package.Manifest.basename, - \\.{{ - \\ .name = .{s}, - \\ .version = "0.0.1", - \\ .minimum_zig_version = "{s}", - \\ .paths = .{{""}}, - \\ .fingerprint = 0x{x}, - \\}} - \\ - , .{ - sanitized_root_name, - build_options.version, - fingerprint.int(), - }) catch |err| switch (err) { - else => fatal("failed to create {q}: {t}", .{ Package.Manifest.basename, err }), - error.PathAlreadyExists => fatal("refusing to overwrite {q}", .{Package.Manifest.basename}), - }; - writeSimpleTemplateFile(io, Package.build_zig_basename, - \\const std = @import("std"); - \\ - \\pub fn build(b: *std.Build) void {{ - \\ _ = b; // stub - \\}} - \\ - , .{}) catch |err| switch (err) { - else => fatal("failed to create {q}: {t}", .{ Package.build_zig_basename, err }), - // `build.zig` already existing is okay: the user has just used `zig init` to set up - // their `build.zig.zon` *after* writing their `build.zig`. So this one isn't fatal. - error.PathAlreadyExists => { - std.log.info("successfully populated {q}, preserving existing {q}", .{ - Package.Manifest.basename, Package.build_zig_basename, - }); - return cleanExit(io); - }, - }; - std.log.info("successfully populated {q} and {q}", .{ Package.Manifest.basename, Package.build_zig_basename }); - return cleanExit(io); - }, - } -} - -fn sanitizeExampleName(arena: Allocator, bytes: []const u8) error{OutOfMemory}![]const u8 { - var result: std.ArrayList(u8) = .empty; - for (bytes, 0..) |byte, i| switch (byte) { - '0'...'9' => { - if (i == 0) try result.append(arena, '_'); - try result.append(arena, byte); - }, - '_', 'a'...'z', 'A'...'Z' => try result.append(arena, byte), - '-', '.', ' ' => try result.append(arena, '_'), - else => continue, - }; - if (!std.zig.isValidId(result.items)) return "foo"; - if (result.items.len > Package.Manifest.max_name_len) - result.shrinkRetainingCapacity(Package.Manifest.max_name_len); - - return result.toOwnedSlice(arena); -} - -test sanitizeExampleName { - var arena_instance = std.heap.ArenaAllocator.init(std.testing.allocator); - defer arena_instance.deinit(); - const arena = arena_instance.allocator(); - - try std.testing.expectEqualStrings("foo_bar", try sanitizeExampleName(arena, "foo bar+")); - try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "")); - try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "!")); - try std.testing.expectEqualStrings("a", try sanitizeExampleName(arena, "!a")); - try std.testing.expectEqualStrings("a_b", try sanitizeExampleName(arena, "a.b!")); - try std.testing.expectEqualStrings("_01234", try sanitizeExampleName(arena, "01234")); - try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "error")); - try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "test")); - try std.testing.expectEqualStrings("tests", try sanitizeExampleName(arena, "tests")); - try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project")); -} - const JitCmdOptions = struct { cmd_name: []const u8, root_src_path: []const u8, @@ -6152,106 +5998,6 @@ fn parseRcIncludes(arg: []const u8) std.zig.RcIncludes { fatal("unsupported rc includes type: {q}", .{arg}); } -const Templates = struct { - zig_lib_directory: Cache.Directory, - dir: Io.Dir, - buffer: std.array_list.Managed(u8), - - fn deinit(templates: *Templates, io: Io) void { - templates.zig_lib_directory.handle.close(io); - templates.dir.close(io); - templates.buffer.deinit(); - templates.* = undefined; - } - - fn write( - templates: *Templates, - arena: Allocator, - io: Io, - out_dir: Io.Dir, - root_name: []const u8, - template_path: []const u8, - fingerprint: Package.Fingerprint, - ) !void { - if (fs.path.dirname(template_path)) |dirname| { - out_dir.createDirPath(io, dirname) catch |err| { - fatal("unable to make path {q}: {t}", .{ dirname, err }); - }; - } - - const max_bytes = 10 * 1024 * 1024; - const contents = templates.dir.readFileAlloc(io, template_path, arena, .limited(max_bytes)) catch |err| { - fatal("unable to read template file {q}: {t}", .{ template_path, err }); - }; - templates.buffer.clearRetainingCapacity(); - try templates.buffer.ensureUnusedCapacity(contents.len); - var i: usize = 0; - while (i < contents.len) { - if (contents[i] == '_' or contents[i] == '.') { - // Both '_' and '.' are allowed because depending on the context - // one prefix will be valid, while the other might not. - if (std.mem.startsWith(u8, contents[i + 1 ..], "NAME")) { - try templates.buffer.appendSlice(root_name); - i += "_NAME".len; - continue; - } else if (std.mem.startsWith(u8, contents[i + 1 ..], "FINGERPRINT")) { - try templates.buffer.print("0x{x}", .{fingerprint.int()}); - i += "_FINGERPRINT".len; - continue; - } else if (std.mem.startsWith(u8, contents[i + 1 ..], "ZIGVER")) { - try templates.buffer.appendSlice(build_options.version); - i += "_ZIGVER".len; - continue; - } - } - - try templates.buffer.append(contents[i]); - i += 1; - } - - return out_dir.writeFile(io, .{ - .sub_path = template_path, - .data = templates.buffer.items, - .flags = .{ .exclusive = true }, - }); - } -}; -fn writeSimpleTemplateFile(io: Io, file_name: []const u8, comptime fmt: []const u8, args: anytype) !void { - const f = try Io.Dir.cwd().createFile(io, file_name, .{ .exclusive = true }); - defer f.close(io); - var buf: [4096]u8 = undefined; - var fw = f.writer(io, &buf); - try fw.interface.print(fmt, args); - try fw.interface.flush(); -} - -fn findTemplates(gpa: Allocator, arena: Allocator, io: Io) Templates { - const cwd_path = std.zig.getResolvedCwd(io, arena) catch |err| { - fatal("unable to get cwd: {t}", .{err}); - }; - const self_exe_path = process.executablePathAlloc(io, arena) catch |err| { - fatal("unable to find self exe path: {t}", .{err}); - }; - var zig_lib_directory = std.zig.findZigLibDirFromSelfExe(arena, io, cwd_path, self_exe_path) catch |err| { - fatal("unable to find zig installation directory {q}: {t}", .{ self_exe_path, err }); - }; - - const s = fs.path.sep_str; - const template_sub_path = "init"; - const template_dir = zig_lib_directory.handle.openDir(io, template_sub_path, .{}) catch |err| { - const path = zig_lib_directory.path orelse "."; - fatal("unable to open zig project template directory '{s}{s}{s}': {t}", .{ - path, s, template_sub_path, err, - }); - }; - - return .{ - .zig_lib_directory = zig_lib_directory, - .dir = template_dir, - .buffer = std.array_list.Managed(u8).init(gpa), - }; -} - fn parseOptimizeMode(s: []const u8) std.lang.OptimizeMode { return stringToEnum(std.lang.OptimizeMode, s) orelse fatal("unrecognized optimization mode: {q}", .{s}); -- 2.54.0 From 98854a673e10bf0606bb720aed93a2b759e8842d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 23 Jun 2026 18:23:59 -0700 Subject: [PATCH 07/49] Maker: it's compiling again --- lib/compiler/Maker.zig | 314 +++++++++++------------- lib/compiler/Maker/Fetch.zig | 14 +- lib/compiler/Maker/Package.zig | 2 +- lib/compiler/Maker/ScannedConfig.zig | 2 +- lib/compiler/Maker/Step.zig | 2 +- lib/compiler/Maker/WebServer.zig | 146 +---------- lib/std/Build/Configuration.zig | 2 +- lib/std/zig.zig | 346 ++++++++++++++++++++++----- lib/std/zig/Server.zig | 2 +- 9 files changed, 438 insertions(+), 392 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index f7eb5b40d14cbfa6c02729435d7a0b27824d8bb8..e0970d180b4faa7f211052c90c06e97ee221c548 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -1,5 +1,6 @@ const Maker = @This(); const builtin = @import("builtin"); +const native_os = builtin.os.tag; const std = @import("std"); const Allocator = std.mem.Allocator; @@ -106,7 +107,7 @@ const MultilineErrors = enum { indent, newline, none }; const Summary = enum { all, new, failures, line, none }; /// Used to build the -M flags to pass to build-exe. -const CliModule = struct { +pub const CliModule = struct { name: []const u8, root_path: []const u8, deps: Deps = .empty, @@ -171,17 +172,17 @@ pub fn main(init: process.Init.Minimal) !void { }; const cmd = stringToEnum(enum { init, fetch, build }, cmd_name) orelse - fatal("bad command name: {q}", .{ cmd_name }); + fatal("bad command name: {q}", .{cmd_name}); switch (cmd) { - .init => return cmdInit( gpa, &graph, args[arg_i..]), - .fetch => return cmdFetch( 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: enum {none, zon, path} = .none; + var print_configuration: enum { none, zon, path } = .none; var override_install_prefix: ?[]const u8 = null; var override_lib_dir: ?[]const u8 = null; var override_bin_dir: ?[]const u8 = null; @@ -409,6 +410,8 @@ pub fn main(init: process.Init.Minimal) !void { 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")) { @@ -551,9 +554,9 @@ pub fn main(init: process.Init.Minimal) !void { io, cwd_path, unresolved_path, - .@"local_cache", + .@"local cache", ) else .{ - .path = try Dir.path.join(arena, &.{build_root.directory.path orelse ".", default_local_zig_cache_basename}), + .path = try Dir.path.join(arena, &.{ build_root.directory.path orelse ".", default_local_zig_cache_basename }), .handle = try build_root.directory.handle.createDirPathOpen(io, default_local_zig_cache_basename, .{}), }; graph.cache = .{ @@ -583,10 +586,13 @@ pub fn main(init: process.Init.Minimal) !void { }); defer main_progress_node.end(); - { + const scanned_config: ScannedConfig = sc: { // Cache lookup for configure options. If we get a match, we can skip // execution of the configure script. If not, we get the file path to pass // to the configure process. + // + // In the hot path, we only check this cache, which means that also + // configure source files need to go in here. var config_man = graph.cache.obtain(); defer config_man.deinit(); @@ -597,28 +603,6 @@ pub fn main(init: process.Init.Minimal) !void { // a `zig build --cache-poison=ignored`. config_man.hash.add(cache_poison == .ignored); - // Normally the build runner is compiled for the host target but here is - // some code to help when debugging edits to the build runner so that you - // can make sure it compiles successfully on other targets. - const resolved_target: Package.Module.ResolvedTarget = t: { - if (debug_target) |triple| { - const target_query = try std.Target.Query.parse(.{ .arch_os_abi = triple }); - config_man.hash.addBytes(triple); - break :t .{ - .result = std.zig.resolveTargetQueryOrFatal(io, target_query), - .is_native_os = false, - .is_native_abi = false, - .is_explicit_dynamic_linker = false, - }; - } - break :t .{ - .result = std.zig.resolveTargetQueryOrFatal(io, .{}), - .is_native_os = true, - .is_native_abi = true, - .is_explicit_dynamic_linker = false, - }; - }; - const pkg_root: Path = if (override_pkg_dir) |p| .initCwd(p) else if (system_pkg_dir_path) |p| @@ -659,10 +643,7 @@ pub fn main(init: process.Init.Minimal) !void { } defer Fork.deinitList(forks.items); - var file_system_inputs: std.ArrayList(u8) = .empty; - defer file_system_inputs.deinit(gpa); - - var build_configurer_argv: std.ArrayList(u8) = .empty; + var build_configurer_argv: std.ArrayList([]const u8) = .empty; defer build_configurer_argv.deinit(gpa); var dependencies_source: std.ArrayList(u8) = .empty; @@ -678,16 +659,28 @@ pub fn main(init: process.Init.Minimal) !void { .sub_path = build_root.build_zig_basename, }; + const configurer_exe_name = "configurer"; + try build_configurer_argv.appendSlice(gpa, &.{ graph.zig_exe, "build-exe", // "--cache-dir", graph.local_cache_root.path orelse ".", // "--global-cache-dir", graph.global_cache_root.path orelse ".", // "--zig-lib-dir", graph.zig_lib_directory.path orelse ".", // - "--name", "configurer", // + "--name", configurer_exe_name, // "-fsingle-threaded", // }); + + // Normally the build runner is compiled for the host target but here is + // some code to help when debugging edits to the build runner so that you + // can make sure it compiles successfully on other targets. + const target_arch_os_abi: ?[]const u8 = if (debug_target) |triple| t: { + config_man.hash.addBytes(triple); + try build_configurer_argv.appendSlice(gpa, &.{ "-target", triple }); + break :t triple; + } else null; + if (graph.libc_file) |libc_file| { - try build_configurer_argv.appendSlice(gpa, &.{ "--libc", libc_file}); + try build_configurer_argv.appendSlice(gpa, &.{ "--libc", libc_file }); } if (graph.reference_trace) |n| { 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 { // We want to release all the locks before executing the child process, so we make a nice // big block here to ensure the cleanup gets run when we extract out our argv. { - - - { const fetch_prog_node = main_progress_node.start("Fetch Packages", 0); defer fetch_prog_node.end(); @@ -820,12 +810,12 @@ pub fn main(init: process.Init.Minimal) !void { var any_unused = false; for (fork_set.keys()) |*fork| { if (fork.uses == 0) { - std.log.err("fork {f} matched no {s} packages", .{ + log.err("fork {f} matched no {s} packages", .{ fork.path, fork.manifest.name, }); any_unused = true; } else { - std.log.info("fork {f} matched {d} {s} packages", .{ + log.info("fork {f} matched {d} {s} packages", .{ fork.path, fork.uses, fork.manifest.name, }); } @@ -842,16 +832,16 @@ pub fn main(init: process.Init.Minimal) !void { process.exit(1); } - if (fetch_only) return cleanExit(io); + if (fetch_only) return process.cleanExit(io); // Create the dependencies.zig file for configurer to // obtain via `@import("@dependencies")`. { { dependencies_source.clearRetainingCapacity(); - var source_writer: Io.Writer.Allocating = .fromArrayList(&dependencies_source); + var source_writer: Io.Writer.Allocating = .fromArrayList(gpa, &dependencies_source); defer dependencies_source = source_writer.toArrayList(); - job_queue.createDependenciesSource(&dependencies_source) catch |err| switch (err) { + job_queue.createDependenciesSource(&source_writer.writer) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, }; } @@ -862,17 +852,18 @@ pub fn main(init: process.Init.Minimal) !void { const hex_digest = hh.final(); const dependencies_zig_path: Path = .{ .root_dir = graph.local_cache_root, - .sub_path = try allocPrint(arena, "o/{s}/dependencies.zig", .{ &hex_digest }), + .sub_path = try allocPrint(arena, "o/{s}/dependencies.zig", .{&hex_digest}), }; var atomic_file = try dependencies_zig_path.root_dir.handle.createFileAtomic( io, - dependencies_zig_path.sub_path, .{ .make_path = true, .replace = true }, + dependencies_zig_path.sub_path, + .{ .make_path = true, .replace = true }, ); defer atomic_file.deinit(io); atomic_file.file.writeStreamingAll(io, dependencies_source.items) catch |err| fatal("writing dependencies.zig contents: {t}", .{err}); atomic_file.replace(io) catch |err| - fatal("replacing {f}: {t}", .{dependencies_zig_path, err}); + fatal("replacing {f}: {t}", .{ dependencies_zig_path, err }); deps_mod.root_path = try dependencies_zig_path.toString(arena); } @@ -914,7 +905,7 @@ pub fn main(init: process.Init.Minimal) !void { global_cache_directory, dep, ) orelse continue; - const dep_mod = job_queue.table.get(dep_digest).?.module orelse continue; + const dep_mod = job_queue.table.get(dep_digest).?.cli_module orelse continue; const name_cloned = try arena.dupe(u8, name); mod.deps.putAssumeCapacityNoClobber(name_cloned, dep_mod); } @@ -948,23 +939,30 @@ pub fn main(init: process.Init.Minimal) !void { try build_configurer_argv.append(gpa, "--listen=-"); - file_system_inputs.clearRetainingCapacity(); - execute_child(build_configurer_argv, &file_system_inputs); - - const hex_digest: []const u8 = &Cache.binToHex(comp.digest.?); - const exe_path: Path = .{ - .root_dir = dirs.local_cache, - .sub_path = try allocPrint(arena, "o/{s}/{s}", .{ hex_digest, comp.emit_bin.? }), + const configure_exe_path: Path = if (std.zig.buildExeSubprocess(gpa, io, .{ + .argv = build_configurer_argv.items, + .cache_root = graph.local_cache_root, + .root_name = configurer_exe_name, + .environ_map = &graph.environ_map, + .cache_manifest = &config_man, + .arch_os_abi = target_arch_os_abi, + })) |p| p else |err| switch (err) { + error.AlreadyReported => process.exit(1), + // If the file system inputs are populated, we can + // still watch for changes and try again. + error.FailedButCacheIntact => @panic("TODO"), + error.Canceled, error.OutOfMemory => |e| return e, }; - _ = try config_man.addFilePath(exe_path, null); - configure_argv.items[0] = try exe_path.toString(arena); + defer gpa.free(configure_exe_path.sub_path); + + configure_argv.items[0] = try configure_exe_path.toString(arena); switch (cache_poison) { .pure, .disallowed, .ignored => if (try config_man.hit()) { const digest = config_man.final(); break :cp .{ .{ - .root_dir = dirs.local_cache, + .root_dir = graph.local_cache_root, .sub_path = try allocPrint(arena, "c/{s}", .{&digest}), }, false, @@ -975,14 +973,15 @@ pub fn main(init: process.Init.Minimal) !void { } if (!process.can_spawn) { - const cmd = try std.mem.join(arena, " ", configure_argv.items); - fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ native_os, cmd }); + fatal("cannot spawn command on {t}: {f}", .{ native_os, @as(std.zig.SubprocessCommand, .{ + .argv = configure_argv.items, + }) }); } const rand_int = randInt(io, u64); const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); const config_tmp_path: Path = .{ - .root_dir = dirs.local_cache, + .root_dir = graph.local_cache_root, .sub_path = tmp_dir_sub_path, }; 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 { const term = term: { const child_node = main_progress_node.start("Run Configure Script", 0); defer child_node.end(); - var child = std.process.spawn(io, .{ + var child = process.spawn(io, .{ .argv = configure_argv.items, .stdout = .{ .file = config_tmp_file }, .progress_node = child_node, @@ -1006,8 +1005,9 @@ pub fn main(init: process.Init.Minimal) !void { }; if (!term.success()) { // Failure to produce the configuration file. - const cmd = try std.mem.join(arena, " ", configure_argv.items); - fatal("the following configure command {f}:\n{s}", .{ term, cmd }); + fatal("configure command {f}: {f}", .{ term, @as(std.zig.SubprocessCommand, .{ + .argv = configure_argv.items, + }) }); } // Even though the file is designed to be sent directly to make // runner, we must load it now because: @@ -1015,17 +1015,16 @@ pub fn main(init: process.Init.Minimal) !void { // add them to `config_man` before obtaining the final digest. // * If it contains a set of lazy packages that need to be // fetched, we need to fetch those now and re-run configure. - var configuration = std.Build.Configuration.loadFile(arena, io, config_tmp_file) catch |err| + var configuration = Configuration.loadFile(arena, io, config_tmp_file) catch |err| fatal("failed to load configuration file {f}: {t}", .{ config_tmp_path, err }); if (configuration.unlazy_deps.len != 0) { - if (!dev.env.supports(.fetch_command)) process.exit(1); var any_errors = false; for (configuration.unlazy_deps) |hash_string| { const hash = hash_string.slice(&configuration); assert(hash.len != 0); if (hash.len > Package.Hash.max_len) { - std.log.err("invalid digest (length {d} exceeds maximum): {q}", .{ hash.len, hash }); + log.err("invalid digest (length {d} exceeds maximum): {q}", .{ hash.len, hash }); any_errors = true; continue; } @@ -1037,33 +1036,17 @@ pub fn main(init: process.Init.Minimal) !void { // cannot be fetched by Zig. const s = Dir.path.sep_str; for (unlazy_set.keys()) |*hash| { - std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ p, hash.toSlice() }); + log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ p, hash.toSlice() }); } - std.log.info("remote package fetching disabled due to --system mode", .{}); - std.log.info("dependencies might be avoidable depending on build configuration", .{}); + log.info("remote package fetching disabled due to --system mode", .{}); + log.info("dependencies might be avoidable depending on build configuration", .{}); process.exit(1); } continue :cp; } - for (configuration.path_deps_base, configuration.path_deps_sub) |base, sub| { - const conf_path: std.Build.Configuration.Path = .{ .base = base, .sub = sub }; - try config_man.addPathPost(conf_path.toCachePath(&configuration, arena)); - } - - // We need to add to the configuration cache the source files of - // configurer itself, so that the maker process can watch the file system - // for those changes and restart itself. By doing this, we make it - // possible to bypass creating a Compilation for configurer on - // Configuration cache hit. - { - var it = mem.splitScalar(u8, file_system_inputs.items, 0); - while (it.next()) |input| { - _ = try config_man.addPrefixedPathPost(.{ - .prefix = input[0], - .sub_path = input[1..], - }); - } + for (configuration.path_deps) |path_dep| { + try config_man.addPathPost(path_dep.toCachePath(&configuration, arena)); } // 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 { } else { const digest = config_man.final(); const final_path: Path = .{ - .root_dir = dirs.local_cache, + .root_dir = graph.local_cache_root, .sub_path = try allocPrint(arena, "c/{s}", .{&digest}), }; Io.Dir.rename( @@ -1107,33 +1090,27 @@ pub fn main(init: process.Init.Minimal) !void { } }; - { - // Release all file system locks just before running the maker process. - var configuration_lock = if (!poisoned) config_man.toOwnedLock() else null; - defer if (configuration_lock) |*l| l.release(io); + // Hang on to the configuration file lock until we finish loading the configuration file. + var configuration_lock = if (!poisoned) config_man.toOwnedLock() else null; + defer if (configuration_lock) |*l| l.release(io); - if (print_configuration_path) { - var stdout_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer); - stdout_writer.interface.print("{f}\n", .{configuration_path}) catch - fatal("failed printing cache file path: {t}", .{stdout_writer.err.?}); - stdout_writer.flush() catch |err| + switch (print_configuration) { + .path => { + initStdoutWriter(io).print("{f}\n", .{configuration_path}) catch + fatal("failed printing cache file path: {t}", .{stdout_writer_allocation.err.?}); + stdout_writer_allocation.flush() catch |err| fatal("failed printing cache file path: {t}", .{err}); - return cleanExit(io); - } - const make_runner = make_runner_task.await(io) catch |err| fatal("failed compiling maker: {t}", .{err}); - - make_argv.items[0] = try make_runner.exe_path.toString(arena); - make_argv.items[argv_index_configuration_file] = try configuration_path.toString(arena); + return process.cleanExit(io); + }, + .none, .zon => {}, } - } - const scanned_config: ScannedConfig = sc: { const configuration = c: { - var file = cwd.openFile(io, configure_path, .{}) catch |err| - fatal("failed to open configuration file {s}: {t}", .{ configure_path, err }); + var file = configuration_path.root_dir.handle.openFile(io, configuration_path.sub_path, .{}) catch |err| + fatal("failed to open configuration file {f}: {t}", .{ configuration_path, err }); defer file.close(io); break :c Configuration.loadFile(arena, io, file) catch |err| - fatal("failed to load configuration file {s}: {t}", .{ configure_path, err }); + fatal("failed to load configuration file {f}: {t}", .{ configuration_path, err }); }; // Technically if the configuration is marked as poisoned, we could // 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 { break :sc .{ .configuration = configuration, .top_level_steps = top_level_steps, - .path = configure_path, + .path = configuration_path, }; }; if (help_menu) { - const w = initStdoutWriter(io); - scanned_config.printUsage(&graph, w) catch |err| switch (err) { + scanned_config.printUsage(&graph, initStdoutWriter(io)) catch |err| switch (err) { error.WriteFailed => return stdout_writer_allocation.err.?, else => |e| return e, }; - w.flush() catch return stdout_writer_allocation.err.?; + try stdout_writer_allocation.flush(); return cleanExit(io, &scanned_config); } else if (steps_menu) { - const w = initStdoutWriter(io); - scanned_config.printSteps(&graph, w) catch |err| switch (err) { + scanned_config.printSteps(&graph, initStdoutWriter(io)) catch |err| switch (err) { error.WriteFailed => return stdout_writer_allocation.err.?, else => |e| return e, }; - w.flush() catch return stdout_writer_allocation.err.?; + try stdout_writer_allocation.flush(); return cleanExit(io, &scanned_config); } else switch (print_configuration) { .none => {}, .zon => { - const w = initStdoutWriter(io); - scanned_config.print(w) catch return stdout_writer_allocation.err.?; - w.flush() catch return stdout_writer_allocation.err.?; + scanned_config.print(initStdoutWriter(io)) catch return stdout_writer_allocation.err.?; + try stdout_writer_allocation.flush(); return cleanExit(io, &scanned_config); }, - .path => { - @panic("TODO"); - }, + .path => unreachable, } if (webui_listen != null) { @@ -1204,7 +1176,7 @@ pub fn main(init: process.Init.Minimal) !void { .root_dir = .cwd(), .sub_path = cwd_relative, } else .{ - .root_dir = build_root_directory, + .root_dir = graph.build_root_directory, .sub_path = "zig-out", }; @@ -1274,7 +1246,7 @@ pub fn main(init: process.Init.Minimal) !void { var w: Watch = w: { if (!watch) break :w undefined; - if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{builtin.os.tag}); + if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{native_os}); break :w try .init(&maker); }; @@ -1366,11 +1338,7 @@ pub fn main(init: process.Init.Minimal) !void { } } -fn cmdFetch( - gpa: Allocator, - graph: *Graph, - args: []const []const u8 -) !void { +fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void { const environ_map = &graph.environ_map; const io = graph.io; const arena = graph.arena; @@ -1392,7 +1360,7 @@ fn cmdFetch( if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { try Io.File.stdout().writeStreamingAll(io, usage_fetch); - return cleanExit(io); + return process.cleanExit(io); } else if (mem.eql(u8, arg, "--global-cache-dir")) { override_global_cache_dir = nextArgOrFatal(args, &arg_i); } else if (mem.eql(u8, arg, "--cache-dir")) { @@ -1500,7 +1468,7 @@ fn cmdFetch( .oom_flag = false, .latest_commit = null, - .module = null, + .cli_module = null, }; defer fetch.deinit(); @@ -1526,10 +1494,10 @@ fn cmdFetch( const name = switch (save) { .no => { var data: [2][]const u8 = .{ package_hash_slice, "\n" }; - const w = initStdoutWriter(); - try w.writeVecAll(&data); - try w.flush(); - return cleanExit(io); + const w = initStdoutWriter(io); + w.writeVecAll(&data) catch return stdout_writer_allocation.err.?; + try stdout_writer_allocation.flush(); + return process.cleanExit(io); }, .yes, .exact => |name| name: { if (name) |n| break :name n; @@ -1567,14 +1535,14 @@ fn cmdFetch( // the refspec may already be fully resolved if (std.mem.eql(u8, target_ref, latest_commit_hex)) break :resolved; - std.log.info("resolved ref {q} to commit {s}", .{ target_ref, latest_commit_hex }); + log.info("resolved ref {q} to commit {s}", .{ target_ref, latest_commit_hex }); // include the original refspec in a query parameter, could be used to check for updates uri.query = .{ .percent_encoded = try allocPrint(arena, "ref={f}", .{ std.fmt.alt(fragment, .formatEscaped), }) }; } else { - std.log.info("resolved to commit {s}", .{latest_commit_hex}); + log.info("resolved to commit {s}", .{latest_commit_hex}); } // replace the refspec with the resolved commit SHA @@ -1613,7 +1581,7 @@ fn cmdFetch( switch (dep.location) { .url => |u| { if (mem.eql(u8, h, package_hash_slice) and mem.eql(u8, u, saved_path_or_url)) { - std.log.info("existing dependency named {q} is up-to-date", .{name}); + log.info("existing dependency named {q} is up-to-date", .{name}); process.exit(0); } }, @@ -1661,7 +1629,7 @@ fn cmdFetch( fatal("unable to write {s} file: {t}", .{ Package.Manifest.basename, err }); }; - return cleanExit(io); + return process.cleanExit(io); } const usage_fetch = @@ -1707,13 +1675,10 @@ const usage_init = \\ ; -fn cmdInit( - gpa: Allocator, - graph: *Graph, - args: []const []const u8 -) !void { +fn cmdInit(gpa: Allocator, graph: *Graph, args: []const []const u8) !void { const arena = graph.arena; const io = graph.io; + const default_build_zig_basename = std.zig.build_zig_basename; var template: enum { example, minimal } = .example; { @@ -1725,7 +1690,7 @@ fn cmdInit( template = .minimal; } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { try Io.File.stdout().writeStreamingAll(io, usage_init); - return cleanExit(io); + return process.cleanExit(io); } else { fatal("unrecognized parameter: {q}", .{arg}); } @@ -1749,7 +1714,7 @@ fn cmdInit( const s = Dir.path.sep_str; const template_paths = [_][]const u8{ - Package.build_zig_basename, + default_build_zig_basename, Package.Manifest.basename, "src" ++ s ++ "main.zig", "src" ++ s ++ "root.zig", @@ -1758,20 +1723,20 @@ fn cmdInit( for (template_paths) |template_path| { if (templates.write(arena, io, Io.Dir.cwd(), sanitized_root_name, template_path, fingerprint)) |_| { - std.log.info("created {s}", .{template_path}); + log.info("created {s}", .{template_path}); ok_count += 1; } else |err| switch (err) { - error.PathAlreadyExists => std.log.info("preserving already existing file: {s}", .{ + error.PathAlreadyExists => log.info("preserving already existing file: {s}", .{ template_path, }), - else => std.log.err("unable to write {s}: {s}\n", .{ template_path, @errorName(err) }), + else => log.err("unable to write {s}: {t}", .{ template_path, err }), } } if (ok_count == template_paths.len) { - std.log.info("see `zig build --help` for a menu of options", .{}); + log.info("see `zig build --help` for a menu of options", .{}); } - return cleanExit(io); + return process.cleanExit(io); }, .minimal => { writeSimpleTemplateFile(io, Package.Manifest.basename, @@ -1791,7 +1756,7 @@ fn cmdInit( else => fatal("failed to create {q}: {t}", .{ Package.Manifest.basename, err }), error.PathAlreadyExists => fatal("refusing to overwrite {q}", .{Package.Manifest.basename}), }; - writeSimpleTemplateFile(io, Package.build_zig_basename, + writeSimpleTemplateFile(io, default_build_zig_basename, \\const std = @import("std"); \\ \\pub fn build(b: *std.Build) void {{ @@ -1799,24 +1764,22 @@ fn cmdInit( \\}} \\ , .{}) catch |err| switch (err) { - else => fatal("failed to create {q}: {t}", .{ Package.build_zig_basename, err }), + else => fatal("failed to create {q}: {t}", .{ default_build_zig_basename, err }), // `build.zig` already existing is okay: the user has just used `zig init` to set up // their `build.zig.zon` *after* writing their `build.zig`. So this one isn't fatal. error.PathAlreadyExists => { - std.log.info("successfully populated {q}, preserving existing {q}", .{ - Package.Manifest.basename, Package.build_zig_basename, + log.info("successfully populated {q}, preserving existing {q}", .{ + Package.Manifest.basename, default_build_zig_basename, }); - return cleanExit(io); + return process.cleanExit(io); }, }; - std.log.info("successfully populated {q} and {q}", .{ Package.Manifest.basename, Package.build_zig_basename }); - return cleanExit(io); + log.info("successfully populated {q} and {q}", .{ Package.Manifest.basename, default_build_zig_basename }); + return process.cleanExit(io); }, } } - - fn markFailedStepsDirty(maker: *Maker) void { const all_steps = maker.step_stack.keys(); @@ -1918,9 +1881,7 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void { } if (any_problems) { if (maker.max_rss_is_default) { - std.log.info("use --maxrss {d} to proceed, risking system memory exhaustion", .{ - max_needed, - }); + log.info("use --maxrss {d} to proceed, risking system memory exhaustion", .{max_needed}); } return error.InsufficientMemory; } @@ -2012,12 +1973,12 @@ fn makeStepNames( } if (fuzz) |mode| blk: { - switch (builtin.os.tag) { + switch (native_os) { // Current implementation depends on two things that need to be ported to Windows: // * Memory-mapping to share data between the fuzzer and build runner. // * COFF/PE support added to `std.debug.Info` (it needs a batching API for resolving // many addresses to source locations). - .windows => fatal("--fuzz not yet implemented for {t}", .{builtin.os.tag}), + .windows => fatal("--fuzz not yet implemented for {t}", .{native_os}), else => {}, } if (@bitSizeOf(usize) != 64) { @@ -2781,23 +2742,23 @@ pub fn printErrorMessages( try writer.writeByte('\n'); } -fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 { +fn nextArg(args: []const []const u8, idx: *usize) ?[]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 { +fn nextArgOrFatal(args: []const []const u8, idx: *usize) []const u8 { return nextArg(args, idx) orelse fatalWithHint("expected argument after {q}", .{args[idx.* - 1]}); } -fn prefixedArgOrFatal(args: []const [:0]const u8, index_ptr: *usize, prefix: []const u8) []const u8 { +fn prefixedArgOrFatal(args: []const []const u8, index_ptr: *usize, prefix: []const u8) []const u8 { const arg = args[index_ptr.*]; if (mem.cutPrefix(u8, arg, prefix)) |rest| return rest; - fatal("expected {q} to begin with {q}", .{arg, prefix}); + fatal("expected {q} to begin with {q}", .{ arg, prefix }); } -fn argsRest(args: []const [:0]const u8, idx: usize) ?[]const [:0]const u8 { +fn argsRest(args: []const []const u8, idx: usize) ?[]const []const u8 { if (idx >= args.len) return null; return args[idx..]; } @@ -3158,8 +3119,8 @@ fn removePoisonedConfiguration(io: Io, scanned_config: *const ScannedConfig) voi if (scanned_config.configuration.poisoned) { // This configuration file was good for only 1 invocation of the maker // process. Delete it to save space on disk. - Io.Dir.cwd().deleteFile(io, scanned_config.path) catch |err| - log.warn("failed deleting poisoned configuration file {s}: {t}", .{ scanned_config.path, err }); + scanned_config.path.root_dir.handle.deleteFile(io, scanned_config.path.sub_path) catch |err| + log.warn("failed deleting poisoned configuration file {f}: {t}", .{ scanned_config.path, err }); } } @@ -3228,8 +3189,8 @@ fn findBuildRoot(arena: Allocator, io: Io, options: FindBuildRootOptions) !Build } else |err| switch (err) { error.FileNotFound => { dirname = Dir.path.dirname(dirname) orelse { - std.log.info("initialize {s} template file with \"zig init\"", .{ std.zig.build_zig_basename }); - std.log.info("see \"zig --help\" for more options", .{}); + log.info("initialize {s} template file with \"zig init\"", .{std.zig.build_zig_basename}); + log.info("see \"zig --help\" for more options", .{}); fatal("no build.zig file found, in the current directory or any parent directories", .{}); }; continue; @@ -3266,7 +3227,7 @@ const Fork = struct { error.Canceled => |e| return e, error.AlreadyReported => fork.failed = true, else => |e| { - std.log.err("failed to load fork at {f}: {t}", .{ fork.path, e }); + log.err("failed to load fork at {f}: {t}", .{ fork.path, e }); fork.failed = true; }, }; @@ -3299,7 +3260,7 @@ const Fork = struct { return error.AlreadyReported; }, else => |e| { - std.log.err("failed to load package manifest {f}: {t}", .{ manifest_path, e }); + log.err("failed to load package manifest {f}: {t}", .{ manifest_path, e }); return error.AlreadyReported; }, }; @@ -3527,4 +3488,3 @@ fn findTemplates(gpa: Allocator, arena: Allocator, io: Io) Templates { .buffer = std.array_list.Managed(u8).init(gpa), }; } - diff --git a/lib/compiler/Maker/Fetch.zig b/lib/compiler/Maker/Fetch.zig index b6093f59b6992efba93f8327348b1145c905c41b..5bd136483ec64ca8ebbe08fe4f7630499d8e4b26 100644 --- a/lib/compiler/Maker/Fetch.zig +++ b/lib/compiler/Maker/Fetch.zig @@ -46,7 +46,7 @@ const ascii = std.ascii; const Allocator = std.mem.Allocator; const Cache = std.Build.Cache; const git = @import("Fetch/git.zig"); -const Package = @import("../Package.zig"); +const Package = @import("Package.zig"); const Manifest = Package.Manifest; const ErrorBundle = std.zig.ErrorBundle; @@ -341,10 +341,10 @@ pub const JobQueue = struct { .{ std.zig.fmtString(name), std.zig.fmtString(h.toSlice()) }, ); } - try w.appendSlice("};\n"); + try w.writeAll("};\n"); } - pub fn createEmptyDependenciesSource(w: *Io.Writer) Io.Writer!void { + pub fn createEmptyDependenciesSource(w: *Io.Writer) Io.Writer.Error!void { try w.writeAll( \\pub const packages = struct {}; \\pub const root_deps: []const struct { []const u8, []const u8 } = &.{}; @@ -858,14 +858,14 @@ pub fn computedPackageHash(f: *const Fetch) Package.Hash { fn checkBuildFileExistence(f: *Fetch) RunError!void { const io = f.job_queue.io; const eb = &f.error_bundle; - if (f.package_root.access(io, Package.build_zig_basename, .{})) |_| { + if (f.package_root.access(io, std.zig.build_zig_basename, .{})) |_| { f.has_build_zig = true; } else |err| switch (err) { error.FileNotFound => {}, else => |e| { try eb.addRootErrorMessage(.{ - .msg = try eb.printString("unable to access '{f}{s}': {t}", .{ - f.package_root, Package.build_zig_basename, e, + .msg = try eb.printString("unable to access {f}/{s}: {t}", .{ + f.package_root, std.zig.build_zig_basename, e, }), }); return error.FetchFailed; @@ -1781,7 +1781,7 @@ fn computeHash(f: *Fetch, pkg_path: Cache.Path, filter: Filter) RunError!Compute )), }; - if (std.mem.eql(u8, entry_pkg_path, Package.build_zig_basename)) + if (std.mem.eql(u8, entry_pkg_path, std.zig.build_zig_basename)) f.has_build_zig = true; const fs_path = try arena.dupe(u8, entry.path); diff --git a/lib/compiler/Maker/Package.zig b/lib/compiler/Maker/Package.zig index 01bcf01036acc109c288727cf6099e9f0d65e94c..7b48056f29121f5d5001fc17f5686812a4052bb5 100644 --- a/lib/compiler/Maker/Package.zig +++ b/lib/compiler/Maker/Package.zig @@ -1,7 +1,7 @@ const std = @import("std"); const assert = std.debug.assert; -pub const Fetch = @import("Package/Fetch.zig"); +pub const Fetch = @import("Fetch.zig"); pub const Manifest = @import("Package/Manifest.zig"); pub const Fingerprint = packed struct(u64) { diff --git a/lib/compiler/Maker/ScannedConfig.zig b/lib/compiler/Maker/ScannedConfig.zig index e52e26279c19eee4b955ea0d6deba574bca47abe..fe42bfd81459fe52420a06ecf3133bfbcb613745 100644 --- a/lib/compiler/Maker/ScannedConfig.zig +++ b/lib/compiler/Maker/ScannedConfig.zig @@ -9,7 +9,7 @@ const Graph = @import("Graph.zig"); configuration: Configuration, top_level_steps: std.array_hash_map.String(Configuration.Step.Index), -path: []const u8, +path: std.Build.Cache.Path, pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void { std.log.err("TODO also print paths", .{}); diff --git a/lib/compiler/Maker/Step.zig b/lib/compiler/Maker/Step.zig index 40d82db57f6afbacc468e038f5c1e9f1487f15d9..a8f0fdb3425c6396530b4a1fe30e2fce81c56131 100644 --- a/lib/compiler/Maker/Step.zig +++ b/lib/compiler/Maker/Step.zig @@ -584,7 +584,7 @@ fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *Zi if (!std.mem.eql(u8, builtin.zig_version_string, body)) { return s.fail( maker, - "zig version mismatch build runner vs compiler: '{s}' vs '{s}'", + "zig version mismatch build runner vs compiler: {q} vs {q}", .{ builtin.zig_version_string, body }, ); } diff --git a/lib/compiler/Maker/WebServer.zig b/lib/compiler/Maker/WebServer.zig index 5191e6ab2d7465de8ff690afd2d904ce48ff6c50..1245b99b9cf9f75f27dd7c83b819dde9f7ab77ac 100644 --- a/lib/compiler/Maker/WebServer.zig +++ b/lib/compiler/Maker/WebServer.zig @@ -582,8 +582,8 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim const cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext"; const maker = ws.maker; - const gpa = maker.gpa; const graph = maker.graph; + const gpa = maker.gpa; const io = graph.io; const main_src_path: Cache.Path = .{ @@ -622,151 +622,13 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim "--listen=-", }); - var child = try std.process.spawn(io, .{ + return std.zig.buildExeSubprocess(gpa, io, .{ .argv = argv.items, - .environ_map = &graph.environ_map, - .stdin = .pipe, - .stdout = .pipe, - .stderr = .pipe, - }); - defer child.kill(io); - - var stderr_task = try io.concurrent(readStreamAlloc, .{ gpa, io, child.stderr.?, .unlimited }); - defer if (stderr_task.cancel(io)) |slice| gpa.free(slice) else |_| {}; - - var stdout_buffer: [512]u8 = undefined; - var stdout_reader: Io.File.Reader = .initStreaming(child.stdout.?, io, &stdout_buffer); - const stdout = &stdout_reader.interface; - - { - var w = child.stdin.?.writer(io, &.{}); - w.interface.writeStruct(std.zig.Client.Message.Header{ .tag = .update, .bytes_len = 0 }, .little) catch |err| switch (err) { - error.WriteFailed => return w.err.?, - }; - w.interface.writeStruct(std.zig.Client.Message.Header{ .tag = .exit, .bytes_len = 0 }, .little) catch |err| switch (err) { - error.WriteFailed => return w.err.?, - }; - } - - const Header = std.zig.Server.Message.Header; - - var result: ?Cache.Path = null; - var result_error_bundle = std.zig.ErrorBundle.empty; - var body_buffer: std.ArrayList(u8) = .empty; - defer body_buffer.deinit(gpa); - - while (true) { - const header = stdout.takeStruct(Header, .little) catch |err| switch (err) { - error.ReadFailed => |e| return e, - error.EndOfStream => break, - }; - body_buffer.clearRetainingCapacity(); - try stdout.appendExact(gpa, &body_buffer, header.bytes_len); - const body = body_buffer.items; - - switch (header.tag) { - .zig_version => { - if (!std.mem.eql(u8, builtin.zig_version_string, body)) { - return error.ZigProtocolVersionMismatch; - } - }, - .error_bundle => { - result_error_bundle = try std.zig.Server.allocErrorBundle(arena, body); - }, - .emit_digest => { - const EmitDigest = std.zig.Server.Message.EmitDigest; - const ebp_hdr: *align(1) const EmitDigest = @ptrCast(body); - if (!ebp_hdr.flags.cache_hit) { - log.info("source changes detected; rebuilt wasm component", .{}); - } - const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len]; - result = .{ - .root_dir = graph.global_cache_root, - .sub_path = try arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*)), - }; - }, - else => {}, // ignore other messages - } - } - - const stderr_contents = try stderr_task.await(io); - if (stderr_contents.len > 0) { - std.debug.print("{s}", .{stderr_contents}); - } - - // Send EOF to stdin. - child.stdin.?.close(io); - child.stdin = null; - - switch (try child.wait(io)) { - .exited => |code| { - if (code != 0) { - log.err( - "the following command exited with error code {d}:\n{s}", - .{ code, try std.zig.allocPrintCmd(arena, argv.items, .{}) }, - ); - return error.WasmCompilationFailed; - } - }, - .signal => |sig| { - log.err( - "the following command terminated with signal {t}:\n{s}", - .{ sig, try std.zig.allocPrintCmd(arena, argv.items, .{}) }, - ); - return error.WasmCompilationFailed; - }, - .stopped => |sig| { - log.err( - "the following command stopped unexpectedly with signal {t}:\n{s}", - .{ sig, try std.zig.allocPrintCmd(arena, argv.items, .{}) }, - ); - return error.WasmCompilationFailed; - }, - .unknown => { - log.err( - "the following command terminated unexpectedly:\n{s}", - .{try std.zig.allocPrintCmd(arena, argv.items, .{})}, - ); - return error.WasmCompilationFailed; - }, - } - - if (result_error_bundle.errorMessageCount() > 0) { - try result_error_bundle.renderToStderr(io, .{}, .auto); - log.err("the following command failed with {d} compilation errors:\n{s}", .{ - result_error_bundle.errorMessageCount(), - try std.zig.allocPrintCmd(arena, argv.items, .{}), - }); - return error.WasmCompilationFailed; - } - - const base_path = result orelse { - log.err("child process failed to report result\n{s}", .{ - try std.zig.allocPrintCmd(arena, argv.items, .{}), - }); - return error.WasmCompilationFailed; - }; - const target = std.zig.system.resolveTargetQuery(io, std.Build.parseTargetQuery(.{ + .cache_root = graph.global_cache_root, + .root_name = root_name, .arch_os_abi = arch_os_abi, .cpu_features = cpu_features, - }) catch unreachable) catch unreachable; - const bin_name = try std.zig.binNameAlloc(arena, .{ - .root_name = root_name, - .cpu_arch = target.cpu.arch, - .os_tag = target.os.tag, - .ofmt = target.ofmt, - .abi = target.abi, - .output_mode = .Exe, }); - return base_path.join(arena, bin_name); -} - -fn readStreamAlloc(gpa: Allocator, io: Io, file: Io.File, limit: Io.Limit) ![]u8 { - var file_reader: Io.File.Reader = .initStreaming(file, io, &.{}); - return file_reader.interface.allocRemaining(gpa, limit) catch |err| switch (err) { - error.ReadFailed => return file_reader.err.?, - else => |e| return e, - }; } pub fn updateTimeReportCompile(ws: *WebServer, opts: struct { diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig index 72b876e577296b359ba86cee7df7d2fe65a8bb92..4a0fc763e043705f120e6d317c59b406ba1a9afd 100644 --- a/lib/std/Build/Configuration.zig +++ b/lib/std/Build/Configuration.zig @@ -1881,7 +1881,7 @@ pub const PathDep = extern struct { _ = c; _ = arena; _ = path; - std.log.err("TODO Configuration.PathDep.toCachePath", .{}); + if (true) @panic("TODO Configuration.PathDep.toCachePath"); } }; diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 2e1e0769436f003a829af900cde18c3b9f440236..17d3d040fa0c85aa804516bf5ea1b0671a47111f 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -7,6 +7,7 @@ const builtin = @import("builtin"); const std = @import("std.zig"); const assert = std.debug.assert; const mem = std.mem; +const log = std.log; const Allocator = std.mem.Allocator; const Io = std.Io; const Writer = std.Io.Writer; @@ -721,7 +722,7 @@ pub fn parseTargetQueryOrReportFatalError( for (diags.arch.?.allCpuModels()) |cpu| { help_text.print(" {s}\n", .{cpu.name}) catch break :help; } - std.log.info("available CPUs for architecture '{s}':\n{s}", .{ + log.info("available CPUs for architecture '{s}':\n{s}", .{ @tagName(diags.arch.?), help_text.items, }); } @@ -734,7 +735,7 @@ pub fn parseTargetQueryOrReportFatalError( for (diags.arch.?.allFeaturesList()) |feature| { help_text.print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help; } - std.log.info("available CPU features for architecture '{s}':\n{s}", .{ + log.info("available CPU features for architecture '{s}':\n{s}", .{ @tagName(diags.arch.?), help_text.items, }); } @@ -747,7 +748,7 @@ pub fn parseTargetQueryOrReportFatalError( inline for (@typeInfo(std.Target.ObjectFormat).@"enum".field_names) |field_name| { help_text.print(" {s}\n", .{field_name}) catch break :help; } - std.log.info("available object formats:\n{s}", .{help_text.items}); + log.info("available object formats:\n{s}", .{help_text.items}); } std.process.fatal("unknown object format: '{s}'", .{opts.object_format.?}); }, @@ -758,7 +759,7 @@ pub fn parseTargetQueryOrReportFatalError( inline for (@typeInfo(std.Target.Cpu.Arch).@"enum".field_names) |field_name| { help_text.print(" {s}\n", .{field_name}) catch break :help; } - std.log.info("available architectures:\n{s} native\n", .{help_text.items}); + log.info("available architectures:\n{s} native\n", .{help_text.items}); } std.process.fatal("unknown architecture: '{s}'", .{diags.unknown_architecture_name.?}); }, @@ -1182,74 +1183,89 @@ pub const ClangCliParam = struct { } }; +/// Deprecated pub const AllocPrintCmdOptions = struct { cwd: ?[]const u8 = null, parent_env: ?*const std.process.Environ.Map = null, child_env: ?*const std.process.Environ.Map = null, }; +/// Deprecated pub fn allocPrintCmd(gpa: Allocator, argv: []const []const u8, options: AllocPrintCmdOptions) Allocator.Error![]u8 { - const shell = struct { - fn escape(writer: *Io.Writer, string: []const u8, is_argv0: bool) !void { - for (string) |c| { - if (switch (c) { - else => true, - '%', '+'...':', '@'...'Z', '_', 'a'...'z' => false, - '=' => is_argv0, - }) break; - } else return writer.writeAll(string); - - try writer.writeByte('"'); - for (string) |c| { - if (switch (c) { - std.ascii.control_code.nul => break, - '!', '"', '$', '\\', '`' => true, - else => !std.ascii.isPrint(c), - }) try writer.writeByte('\\'); - switch (c) { - std.ascii.control_code.nul => unreachable, - std.ascii.control_code.bel => try writer.writeByte('a'), - std.ascii.control_code.bs => try writer.writeByte('b'), - std.ascii.control_code.ht => try writer.writeByte('t'), - std.ascii.control_code.lf => try writer.writeByte('n'), - std.ascii.control_code.vt => try writer.writeByte('v'), - std.ascii.control_code.ff => try writer.writeByte('f'), - std.ascii.control_code.cr => try writer.writeByte('r'), - std.ascii.control_code.esc => try writer.writeByte('E'), - ' '...'~' => try writer.writeByte(c), - else => try writer.print("{o:0>3}", .{c}), - } - } - try writer.writeByte('"'); - } - }; - var aw: Io.Writer.Allocating = .init(gpa); defer aw.deinit(); - const writer = &aw.writer; - if (options.cwd) |path| { - writer.print("cd {s} && ", .{path}) catch return error.OutOfMemory; - } - if (options.child_env) |child_env| { - for (child_env.keys(), child_env.values()) |key, value| { - if (options.parent_env) |parent_env| { - if (parent_env.get(key)) |process_value| { - if (std.mem.eql(u8, value, process_value)) continue; - } - } - writer.print("{s}=", .{key}) catch return error.OutOfMemory; - shell.escape(writer, value, false) catch return error.OutOfMemory; - writer.writeByte(' ') catch return error.OutOfMemory; - } - } - shell.escape(writer, argv[0], true) catch return error.OutOfMemory; - for (argv[1..]) |arg| { - writer.writeByte(' ') catch return error.OutOfMemory; - shell.escape(writer, arg, false) catch return error.OutOfMemory; - } + SubprocessCommand.format(.{ + .argv = argv, + .cwd = options.cwd, + .parent_env = options.parent_env, + .child_env = options.child_env, + }, &aw.writer) catch return error.OutOfMemory; return aw.toOwnedSlice(); } +fn shellEscape(writer: *Io.Writer, string: []const u8, is_argv0: bool) !void { + for (string) |c| { + if (switch (c) { + else => true, + '%', '+'...':', '@'...'Z', '_', 'a'...'z' => false, + '=' => is_argv0, + }) break; + } else return writer.writeAll(string); + + try writer.writeByte('"'); + for (string) |c| { + if (switch (c) { + std.ascii.control_code.nul => break, + '!', '"', '$', '\\', '`' => true, + else => !std.ascii.isPrint(c), + }) try writer.writeByte('\\'); + switch (c) { + std.ascii.control_code.nul => unreachable, + std.ascii.control_code.bel => try writer.writeByte('a'), + std.ascii.control_code.bs => try writer.writeByte('b'), + std.ascii.control_code.ht => try writer.writeByte('t'), + std.ascii.control_code.lf => try writer.writeByte('n'), + std.ascii.control_code.vt => try writer.writeByte('v'), + std.ascii.control_code.ff => try writer.writeByte('f'), + std.ascii.control_code.cr => try writer.writeByte('r'), + std.ascii.control_code.esc => try writer.writeByte('E'), + ' '...'~' => try writer.writeByte(c), + else => try writer.print("{o:0>3}", .{c}), + } + } + try writer.writeByte('"'); +} + +pub const SubprocessCommand = struct { + argv: []const []const u8, + cwd: ?[]const u8 = null, + parent_env: ?*const std.process.Environ.Map = null, + child_env: ?*const std.process.Environ.Map = null, + + pub fn format(sc: SubprocessCommand, w: *Io.Writer) Io.Writer.Error!void { + if (sc.cwd) |path| { + try w.print("cd {s} && ", .{path}); + } + if (sc.child_env) |child_env| { + for (child_env.keys(), child_env.values()) |key, value| { + if (sc.parent_env) |parent_env| { + if (parent_env.get(key)) |process_value| { + if (std.mem.eql(u8, value, process_value)) continue; + } + } + try w.print("{s}=", .{key}); + try shellEscape(w, value, false); + try w.writeByte(' '); + } + } + try shellEscape(w, sc.argv[0], true); + for (sc.argv[1..]) |arg| { + try w.writeByte(' '); + try shellEscape(w, arg, false); + } + } +}; + /// Like `std.process.currentPathAlloc`, but also resolves the path with `Dir.path.resolve`. This /// means the path has no repeated separators, no "." or ".." components, and no trailing separator. /// On WASI, "" is returned instead of ".". @@ -1391,7 +1407,7 @@ pub const Directories = struct { }, }; } - fn openUnresolved( + pub fn openUnresolved( arena: Allocator, io: Io, cwd: []const u8, @@ -1609,6 +1625,214 @@ pub fn isUpDir(p: []const u8) bool { return mem.startsWith(u8, p, "..") and (p.len == 2 or p[2] == Dir.path.sep); } +pub const BuildExeSubprocessOptions = struct { + argv: []const []const u8, + cache_root: Cache.Directory, + root_name: []const u8, + + environ_map: ?*std.process.Environ.Map = null, + cache_manifest: ?*Cache.Manifest = null, + arch_os_abi: ?[]const u8 = null, + cpu_features: ?[]const u8 = null, +}; + +pub const BuildExeSubprocessError = error{ + /// Error message has been logged. + AlreadyReported, + /// Error message has been logged, and source files added to the `Cache.Manifest`. + FailedButCacheIntact, +} || Io.Cancelable || Allocator.Error; + +/// Assumes `argv` has `--listen=-` in it and the child process is `zig build-exe`. +/// +/// Result path is allocated via gpa. +pub fn buildExeSubprocess(gpa: Allocator, io: Io, options: BuildExeSubprocessOptions) BuildExeSubprocessError!Cache.Path { + const cmd: SubprocessCommand = .{ .argv = options.argv }; + + var child = std.process.spawn(io, .{ + .argv = options.argv, + .environ_map = options.environ_map, + .stdin = .pipe, + .stdout = .pipe, + .stderr = .pipe, + }) catch |err| { + log.err("spawning command {t}: {f}", .{ err, cmd }); + return error.AlreadyReported; + }; + defer child.kill(io); + + var stderr_task = io.concurrent(readStreamAlloc, .{ gpa, io, child.stderr.?, .unlimited }) catch + @panic("TODO use multireader instead"); + defer if (stderr_task.cancel(io)) |slice| gpa.free(slice) else |_| {}; + + var stdout_buffer: [512]u8 = undefined; + var stdout_reader: Io.File.Reader = .initStreaming(child.stdout.?, io, &stdout_buffer); + const stdout = &stdout_reader.interface; + + { + var w = child.stdin.?.writer(io, &.{}); + w.interface.writeStruct(Client.Message.Header{ .tag = .update, .bytes_len = 0 }, .little) catch |err| switch (err) { + error.WriteFailed => { + log.err("{t} writing to command: {f}", .{ w.err.?, cmd }); + return error.AlreadyReported; + }, + }; + w.interface.writeStruct(Client.Message.Header{ .tag = .exit, .bytes_len = 0 }, .little) catch |err| switch (err) { + error.WriteFailed => { + log.err("{t} writing to command: {f}", .{ w.err.?, cmd }); + return error.AlreadyReported; + }, + }; + } + + const Header = Server.Message.Header; + + var result: ?Cache.Path = null; + defer if (result) |r| gpa.free(r.sub_path); + + var result_error_bundle: ErrorBundle = .empty; + defer result_error_bundle.deinit(gpa); + + var body_buffer: std.ArrayList(u8) = .empty; + defer body_buffer.deinit(gpa); + + var received_fs_inputs = false; + + while (true) { + const header = stdout.takeStruct(Header, .little) catch |err| switch (err) { + error.ReadFailed => { + log.err("{t} reading from command: {f}", .{ stdout_reader.err.?, cmd }); + return error.AlreadyReported; + }, + error.EndOfStream => break, + }; + body_buffer.clearRetainingCapacity(); + stdout.appendExact(gpa, &body_buffer, header.bytes_len) catch |err| switch (err) { + error.ReadFailed => { + log.err("{t} reading from command: {f}", .{ stdout_reader.err.?, cmd }); + return error.AlreadyReported; + }, + error.OutOfMemory => |e| return e, + error.EndOfStream => { + log.err("unexpected end of stream from command: {f}", .{cmd}); + return error.AlreadyReported; + }, + }; + const body = body_buffer.items; + + switch (header.tag) { + .zig_version => { + if (!std.mem.eql(u8, builtin.zig_version_string, body)) { + log.err("zig protocol version mismatch from command: {f}", .{cmd}); + return error.AlreadyReported; + } + }, + .error_bundle => { + result_error_bundle.deinit(gpa); + result_error_bundle = Server.allocErrorBundle(gpa, body) catch |err| switch (err) { + error.EndOfStream => break, + else => |e| return e, + }; + }, + .emit_digest => { + const EmitDigest = Server.Message.EmitDigest; + const ebp_hdr: *align(1) const EmitDigest = @ptrCast(body); + if (!ebp_hdr.flags.cache_hit) { + log.info("source changes detected; rebuilt {s}", .{options.root_name}); + } + const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len]; + if (result) |r| gpa.free(r.sub_path); + result = .{ + .root_dir = options.cache_root, + .sub_path = try Dir.path.join(gpa, &.{ "o", &Cache.binToHex(digest.*) }), + }; + }, + .file_system_inputs => { + received_fs_inputs = true; + @panic("TODO"); + //var it = mem.splitScalar(u8, file_system_inputs.items, 0); + //while (it.next()) |input| { + // _ = try config_man.addPrefixedPathPost(.{ + // .prefix = input[0], + // .sub_path = input[1..], + // }); + //} + }, + else => {}, // ignore other messages + } + } + + const stderr_contents = stderr_task.await(io) catch |err| switch (err) { + error.Canceled, error.OutOfMemory => |e| return e, + else => |e| c: { + log.warn("{t} reading stderr from command: {f}", .{ e, cmd }); + break :c ""; + }, + }; + if (stderr_contents.len > 0) + log.warn("unexpected stderr from {s} command:\n{s}", .{ options.argv[0], stderr_contents }); + + // Send EOF to stdin. + child.stdin.?.close(io); + child.stdin = null; + + const term = child.wait(io) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| { + log.err("{t} waiting for command: {f}", .{ e, cmd }); + return error.AlreadyReported; + }, + }; + + if (!term.success()) { + log.err("command {f}: {f}", .{ term, cmd }); + if (received_fs_inputs) return error.FailedButCacheIntact; + return error.AlreadyReported; + } + + if (result_error_bundle.errorMessageCount() > 0) { + result_error_bundle.renderToStderr(io, .{}, .auto) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| { + log.err("failed rendering error bundle: {t}", .{e}); + return error.AlreadyReported; + }, + }; + log.err("{s} command reported {d} compilation errors: {f}", .{ + options.argv[0], result_error_bundle.errorMessageCount(), cmd, + }); + if (received_fs_inputs) return error.FailedButCacheIntact; + return error.AlreadyReported; + } + + const base_path = result orelse { + log.err("command failed to report result: {f}", .{cmd}); + return error.AlreadyReported; + }; + const parsed_target = system.resolveTargetQuery(io, std.Build.parseTargetQuery(.{ + .arch_os_abi = options.arch_os_abi orelse "native", + .cpu_features = options.cpu_features, + }) catch unreachable) catch unreachable; + const bin_name = try binNameAlloc(gpa, .{ + .root_name = options.root_name, + .cpu_arch = parsed_target.cpu.arch, + .os_tag = parsed_target.os.tag, + .ofmt = parsed_target.ofmt, + .abi = parsed_target.abi, + .output_mode = .Exe, + }); + defer gpa.free(bin_name); + return base_path.join(gpa, bin_name); +} + +fn readStreamAlloc(gpa: Allocator, io: Io, file: Io.File, limit: Io.Limit) ![]u8 { + var file_reader: Io.File.Reader = .initStreaming(file, io, &.{}); + return file_reader.interface.allocRemaining(gpa, limit) catch |err| switch (err) { + error.ReadFailed => return file_reader.err.?, + else => |e| return e, + }; +} + test { _ = Ast; _ = AstRlAnnotate; diff --git a/lib/std/zig/Server.zig b/lib/std/zig/Server.zig index 96679d4fc315459d963f694298bfd8df549a7502..cf43cb0af2822cf416868dd1eba76bcb06b791a7 100644 --- a/lib/std/zig/Server.zig +++ b/lib/std/zig/Server.zig @@ -264,7 +264,7 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { try s.out.flush(); } -pub fn allocErrorBundle(gpa: std.mem.Allocator, body: []const u8) error{ OutOfMemory, EndOfStream }!std.zig.ErrorBundle { +pub fn allocErrorBundle(gpa: Allocator, body: []const u8) error{ OutOfMemory, EndOfStream }!std.zig.ErrorBundle { var r: Reader = .fixed(body); const hdr = r.takeStruct(OutMessage.ErrorBundle, .little) catch |err| switch (err) { error.EndOfStream => |e| return e, -- 2.54.0 From ad4887d1b8f4d439f154df15e219c98676b5334d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 23 Jun 2026 22:08:34 -0700 Subject: [PATCH 08/49] Maker: fix arg parsing init subcommand works now --- lib/compiler/Maker.zig | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index e0970d180b4faa7f211052c90c06e97ee221c548..9f90bf0722fc221569b8e756abd205511797fdbc 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -132,7 +132,6 @@ pub fn main(init: process.Init.Minimal) !void { defer threaded.deinit(); const io = threaded.io(); - // ...but we'll back our arena by `std.heap.page_allocator` for efficiency. 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()}); @@ -2742,20 +2741,20 @@ pub fn printErrorMessages( try writer.writeByte('\n'); } -fn nextArg(args: []const []const u8, idx: *usize) ?[]const u8 { - if (idx.* >= args.len) return null; - defer idx.* += 1; - return args[idx.*]; +fn nextArg(args: []const []const u8, i: *usize) ?[]const u8 { + if (i.* >= args.len) return null; + defer i.* += 1; + return args[i.*]; } -fn nextArgOrFatal(args: []const []const u8, idx: *usize) []const u8 { - return nextArg(args, idx) orelse fatalWithHint("expected argument after {q}", .{args[idx.* - 1]}); +fn nextArgOrFatal(args: []const []const u8, i: *usize) []const u8 { + return nextArg(args, i) orelse fatalWithHint("expected another argument after {q}", .{args[i.* - 1]}); } -fn prefixedArgOrFatal(args: []const []const u8, index_ptr: *usize, prefix: []const u8) []const u8 { - const arg = args[index_ptr.*]; +fn prefixedArgOrFatal(args: []const []const u8, i: *usize, prefix: []const u8) []const u8 { + const arg = nextArgOrFatal(args, i); if (mem.cutPrefix(u8, arg, prefix)) |rest| return rest; - fatal("expected {q} to begin with {q}", .{ arg, prefix }); + fatal("expected {q} to instead begin with {q}", .{ arg, prefix }); } fn argsRest(args: []const []const u8, idx: usize) ?[]const []const u8 { -- 2.54.0 From 20e7cc8d88dc1490d8635db0e6f96fff194763e6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 23 Jun 2026 22:40:09 -0700 Subject: [PATCH 09/49] std.Build.Cache: bump max number of prefixes --- lib/compiler/Maker.zig | 2 +- lib/std/Build/Cache.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 9f90bf0722fc221569b8e756abd205511797fdbc..42c5a80f588fc1665ebc78a015c776033cbf199c 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -566,9 +566,9 @@ pub fn main(init: process.Init.Minimal) !void { }; graph.cache.addPrefix(.{ .path = null, .handle = cwd }); graph.cache.addPrefix(graph.build_root_directory); - graph.cache.addPrefix(zig_lib_directory); graph.cache.addPrefix(graph.local_cache_root); graph.cache.addPrefix(global_cache_directory); + graph.cache.addPrefix(zig_lib_directory); graph.cache.hash.addBytes(builtin.zig_version_string); const NO_COLOR = EnvVar.NO_COLOR.isSet(&graph.environ_map); diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 68f5cfc49af5e8e48724db42455115313108f9e1..724c1fc4322c7761583b6125c6d16146613790da 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -28,7 +28,7 @@ mutex: Io.Mutex = .init, /// are replaced with single-character indicators. This is not to save /// space but to eliminate absolute file paths. This improves portability /// and usefulness of the cache for advanced use cases. -prefixes_buffer: [4]Directory = undefined, +prefixes_buffer: [5]Directory = undefined, prefixes_len: usize = 0, /// Used to identify prefixes. References external memory. cwd: []const u8, -- 2.54.0 From 74168de68b8ebd6cb40cd9ab1c0c44d2e01012d4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 23 Jun 2026 23:10:41 -0700 Subject: [PATCH 10/49] Maker: fix lowering of build module on CLI --- lib/compiler/Maker.zig | 33 ++++++++++++++------------------- lib/std/zig.zig | 4 +--- src/Compilation.zig | 8 +++----- 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 42c5a80f588fc1665ebc78a015c776033cbf199c..b8db4f100f3a81ee7c722e3db70ce09e34290047 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -113,6 +113,15 @@ pub const CliModule = struct { deps: Deps = .empty, const Deps = std.array_hash_map.String(*CliModule); + + fn lower(cm: *const CliModule, arena: Allocator, gpa: Allocator, argv: *std.ArrayList([]const u8)) !void { + try argv.ensureUnusedCapacity(gpa, 2 * cm.deps.count() + 1); + for (cm.deps.values()) |dep| { + argv.appendAssumeCapacity("--dep"); + argv.appendAssumeCapacity(dep.name); + } + argv.appendAssumeCapacity(try allocPrint(arena, "-M{s}={s}", .{ cm.name, cm.root_path })); + } }; pub fn main(init: process.Init.Minimal) !void { @@ -691,19 +700,12 @@ pub fn main(init: process.Init.Minimal) !void { "--dep", "@build", // "--dep", "@dependencies", // try allocPrint(arena, "-Mroot={f}", .{configurer_root_src_path}), // - try allocPrint(arena, "-M@build={f}", .{root_build_src_path}), // }); // In the loop below, after doing the fetch operation, the argv will be // truncated at this point, dependencies added, and then the // "--listen=-" arg appended at the end. - const argv_deps_index = build_configurer_argv.items.len - 1; - - //const root_mod = try arena.create(CliModule); - //root_mod.* = .{ - // .name = "root", - // .root_path = try configurer_root_src_path.toString(arena), - //}; + const argv_deps_index = build_configurer_argv.items.len; const build_mod = try arena.create(CliModule); build_mod.* = .{ @@ -722,7 +724,6 @@ pub fn main(init: process.Init.Minimal) !void { // This loop is re-evaluated when the build script exits with an indication that it // could not continue due to missing lazy dependencies. const configuration_path: Path, const poisoned: bool = cp: while (true) { - //root_mod.deps.clearRetainingCapacity(); build_mod.deps.clearRetainingCapacity(); deps_mod.deps.clearRetainingCapacity(); @@ -923,21 +924,15 @@ pub fn main(init: process.Init.Minimal) !void { dep.name, dep.root_path, })); } - try build_configurer_argv.ensureUnusedCapacity(gpa, 2 * deps_mod.deps.count() + 1); - for (deps_mod.deps.values()) |dep| { - build_configurer_argv.appendAssumeCapacity("--dep"); - build_configurer_argv.appendAssumeCapacity(dep.name); - } - build_configurer_argv.appendAssumeCapacity(try allocPrint(arena, "-M@dependencies={s}", .{ - deps_mod.root_path, - })); + try deps_mod.lower(arena, gpa, &build_configurer_argv); + try build_mod.lower(arena, gpa, &build_configurer_argv); + + try build_configurer_argv.append(gpa, "--listen=-"); } const compile_prog_node = main_progress_node.start("Compile Configure Script", 0); defer compile_prog_node.end(); - try build_configurer_argv.append(gpa, "--listen=-"); - const configure_exe_path: Path = if (std.zig.buildExeSubprocess(gpa, io, .{ .argv = build_configurer_argv.items, .cache_root = graph.local_cache_root, diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 17d3d040fa0c85aa804516bf5ea1b0671a47111f..28bc16a02f91abc8f1a9626bfb8fdc6feb688cd4 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -1798,9 +1798,7 @@ pub fn buildExeSubprocess(gpa: Allocator, io: Io, options: BuildExeSubprocessOpt return error.AlreadyReported; }, }; - log.err("{s} command reported {d} compilation errors: {f}", .{ - options.argv[0], result_error_bundle.errorMessageCount(), cmd, - }); + log.err("command reported {d} compilation errors: {f}", .{ result_error_bundle.errorMessageCount(), cmd }); if (received_fs_inputs) return error.FailedButCacheIntact; return error.AlreadyReported; } diff --git a/src/Compilation.zig b/src/Compilation.zig index b130d4917c17a86a856f0ab02ee4552ddd179bda..55593959ab44b807ed69fa2f3cecfcbfe76542d5 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2766,11 +2766,9 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE .file_open, .file_stat, .file_read, .file_hash => |op| { const pp = man.files.keys()[op.file_index].prefixed_path; const prefix = man.cache.prefixes()[pp.prefix]; - return comp.setMiscFailure( - .check_whole_cache, - "failed to check cache: '{f}{s}' {t} {t}", - .{ prefix, pp.sub_path, man.diagnostic, op.err }, - ); + return comp.setMiscFailure(.check_whole_cache, "failed to check cache: {f}{s} {t} {t}", .{ + prefix, pp.sub_path, man.diagnostic, op.err, + }); }, }, error.OutOfMemory, error.Canceled => |e| return e, -- 2.54.0 From 9add673f584a514b2e519f679fb0f6baf71daab7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 23 Jun 2026 23:15:49 -0700 Subject: [PATCH 11/49] Maker: fix path to configurer.zig --- lib/compiler/Maker.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index b8db4f100f3a81ee7c722e3db70ce09e34290047..78da7c5d74111927f2947392d6fa161daf3b379c 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -574,10 +574,10 @@ pub fn main(init: process.Init.Minimal) !void { .cwd = cwd_path, }; graph.cache.addPrefix(.{ .path = null, .handle = cwd }); - graph.cache.addPrefix(graph.build_root_directory); + graph.cache.addPrefix(zig_lib_directory); graph.cache.addPrefix(graph.local_cache_root); graph.cache.addPrefix(global_cache_directory); - graph.cache.addPrefix(zig_lib_directory); + graph.cache.addPrefix(graph.build_root_directory); graph.cache.hash.addBytes(builtin.zig_version_string); const NO_COLOR = EnvVar.NO_COLOR.isSet(&graph.environ_map); @@ -659,7 +659,7 @@ pub fn main(init: process.Init.Minimal) !void { const configurer_root_src_path: Cache.Path = .{ .root_dir = graph.zig_lib_directory, - .sub_path = "lib/compiler/configurer.zig", + .sub_path = "compiler/configurer.zig", }; const root_build_src_path: Cache.Path = .{ -- 2.54.0 From c8dad46ced6afcf08dfac2b8d4ae0c3a062983be Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 23 Jun 2026 23:32:29 -0700 Subject: [PATCH 12/49] Maker: add configure file system inputs to configuration cache also wire up progress node to child --- lib/compiler/Maker.zig | 7 ++++++ lib/compiler/Maker/WebServer.zig | 4 ++++ lib/std/Build/Cache.zig | 4 ++-- lib/std/zig.zig | 39 ++++++++++++++++++++------------ 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 78da7c5d74111927f2947392d6fa161daf3b379c..74a7f4d4a37b6b244c27b1e876cfb83c2715bf6c 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -573,11 +573,17 @@ pub fn main(init: process.Init.Minimal) !void { .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 == @intFromEnum(std.zig.Server.Message.PathPrefix.cwd)); + comptime assert(1 == @intFromEnum(std.zig.Server.Message.PathPrefix.zig_lib)); + comptime assert(2 == @intFromEnum(std.zig.Server.Message.PathPrefix.local_cache)); + comptime assert(3 == @intFromEnum(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); @@ -940,6 +946,7 @@ pub fn main(init: process.Init.Minimal) !void { .environ_map = &graph.environ_map, .cache_manifest = &config_man, .arch_os_abi = target_arch_os_abi, + .progress_node = compile_prog_node, })) |p| p else |err| switch (err) { error.AlreadyReported => process.exit(1), // If the file system inputs are populated, we can diff --git a/lib/compiler/Maker/WebServer.zig b/lib/compiler/Maker/WebServer.zig index 1245b99b9cf9f75f27dd7c83b819dde9f7ab77ac..dddea1600beef0e0187162584349c5c0cd1df84d 100644 --- a/lib/compiler/Maker/WebServer.zig +++ b/lib/compiler/Maker/WebServer.zig @@ -622,12 +622,16 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim "--listen=-", }); + const compile_prog_node = ws.root_prog_node.start("Compile WebAssembly Component", 0); + defer compile_prog_node.end(); + return std.zig.buildExeSubprocess(gpa, io, .{ .argv = argv.items, .cache_root = graph.global_cache_root, .root_name = root_name, .arch_os_abi = arch_os_abi, .cpu_features = cpu_features, + .progress_node = compile_prog_node, }); } diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 724c1fc4322c7761583b6125c6d16146613790da..b3283856d9e64339cbaf153601aa80a0265bcefb 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -91,13 +91,13 @@ fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath { }; // Free the resolved path since we're not going to return it gpa.free(resolved_path); - return PrefixedPath{ + return .{ .prefix = i, .sub_path = sub_path, }; } - return PrefixedPath{ + return .{ .prefix = 0, .sub_path = resolved_path, }; diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 28bc16a02f91abc8f1a9626bfb8fdc6feb688cd4..6efe0cc7431e367f19add7b142db99641e6728b7 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -1250,7 +1250,7 @@ pub const SubprocessCommand = struct { for (child_env.keys(), child_env.values()) |key, value| { if (sc.parent_env) |parent_env| { if (parent_env.get(key)) |process_value| { - if (std.mem.eql(u8, value, process_value)) continue; + if (mem.eql(u8, value, process_value)) continue; } } try w.print("{s}=", .{key}); @@ -1364,10 +1364,10 @@ pub const Directories = struct { const local_cache = getLocalCacheDirectory(arena, io, cwd, global_cache, local_cache_strat); - if (std.mem.eql(u8, zig_lib.path orelse "", global_cache.path orelse "")) { + if (mem.eql(u8, zig_lib.path orelse "", global_cache.path orelse "")) { fatal("zig lib directory '{f}' cannot be equal to global cache directory '{f}'", .{ zig_lib, global_cache }); } - if (std.mem.eql(u8, zig_lib.path orelse "", local_cache.path orelse "")) { + if (mem.eql(u8, zig_lib.path orelse "", local_cache.path orelse "")) { fatal("zig lib directory '{f}' cannot be equal to local cache directory '{f}'", .{ zig_lib, local_cache }); } @@ -1400,7 +1400,7 @@ pub const Directories = struct { fn getPreopen(preopens: std.process.Preopens, name: []const u8) Cache.Directory { return .{ - .path = if (std.mem.eql(u8, name, ".")) null else name, + .path = if (mem.eql(u8, name, ".")) null else name, .handle = switch (preopens.get(name) orelse fatal("preopen not found: {q}", .{name})) { .file => fatal("preopen {q} is not a directory", .{name}), .dir => |d| d, @@ -1607,7 +1607,7 @@ pub fn resolvePath( assert(Dir.path.isAbsolute(path_resolved)); assert(Dir.path.isAbsolute(cwd_resolved)); - if (!std.mem.startsWith(u8, path_resolved, cwd_resolved)) return path_resolved; // not in cwd + if (!mem.startsWith(u8, path_resolved, cwd_resolved)) return path_resolved; // not in cwd if (path_resolved.len == cwd_resolved.len) { // equal to cwd gpa.free(path_resolved); @@ -1634,6 +1634,7 @@ pub const BuildExeSubprocessOptions = struct { cache_manifest: ?*Cache.Manifest = null, arch_os_abi: ?[]const u8 = null, cpu_features: ?[]const u8 = null, + progress_node: std.Progress.Node = .none, }; pub const BuildExeSubprocessError = error{ @@ -1655,6 +1656,7 @@ pub fn buildExeSubprocess(gpa: Allocator, io: Io, options: BuildExeSubprocessOpt .stdin = .pipe, .stdout = .pipe, .stderr = .pipe, + .progress_node = options.progress_node, }) catch |err| { log.err("spawning command {t}: {f}", .{ err, cmd }); return error.AlreadyReported; @@ -1722,7 +1724,7 @@ pub fn buildExeSubprocess(gpa: Allocator, io: Io, options: BuildExeSubprocessOpt switch (header.tag) { .zig_version => { - if (!std.mem.eql(u8, builtin.zig_version_string, body)) { + if (!mem.eql(u8, builtin.zig_version_string, body)) { log.err("zig protocol version mismatch from command: {f}", .{cmd}); return error.AlreadyReported; } @@ -1747,16 +1749,23 @@ pub fn buildExeSubprocess(gpa: Allocator, io: Io, options: BuildExeSubprocessOpt .sub_path = try Dir.path.join(gpa, &.{ "o", &Cache.binToHex(digest.*) }), }; }, - .file_system_inputs => { + .file_system_inputs => if (options.cache_manifest) |man| { received_fs_inputs = true; - @panic("TODO"); - //var it = mem.splitScalar(u8, file_system_inputs.items, 0); - //while (it.next()) |input| { - // _ = try config_man.addPrefixedPathPost(.{ - // .prefix = input[0], - // .sub_path = input[1..], - // }); - //} + var it = mem.splitScalar(u8, body, 0); + while (it.next()) |prefixed_path| { + const prefix: Server.Message.PathPrefix = @enumFromInt(prefixed_path[0] - 1); + const sub_path = prefixed_path[1..]; + _ = man.addPrefixedPathPost(.{ + .prefix = @intFromEnum(prefix), + .sub_path = sub_path, + }) catch |err| switch (err) { + error.Canceled, error.OutOfMemory => |e| return e, + else => |e| { + log.err("adding {t} {s} to cache failed: {t}", .{ prefix, sub_path, e }); + return error.AlreadyReported; + }, + }; + } }, else => {}, // ignore other messages } -- 2.54.0 From a69438f95cca0af4f3cc81237777e565c6b07efc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 23 Jun 2026 23:41:17 -0700 Subject: [PATCH 13/49] Maker: only build configurer on cache miss This is now possible thanks to putting the configurer source files also into the configuration cache manifest. --- lib/compiler/Maker.zig | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 74a7f4d4a37b6b244c27b1e876cfb83c2715bf6c..fe27a158800a7988690c7611a27c0f00a52e01fa 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -939,6 +939,20 @@ pub fn main(init: process.Init.Minimal) !void { const compile_prog_node = main_progress_node.start("Compile Configure Script", 0); defer compile_prog_node.end(); + switch (cache_poison) { + .pure, .disallowed, .ignored => if (try config_man.hit()) { + const digest = config_man.final(); + break :cp .{ + .{ + .root_dir = graph.local_cache_root, + .sub_path = try allocPrint(arena, "c/{s}", .{&digest}), + }, + false, + }; + }, + .poisoned => {}, // Don't bother checking for cache hit. + } + const configure_exe_path: Path = if (std.zig.buildExeSubprocess(gpa, io, .{ .argv = build_configurer_argv.items, .cache_root = graph.local_cache_root, @@ -957,20 +971,6 @@ pub fn main(init: process.Init.Minimal) !void { defer gpa.free(configure_exe_path.sub_path); configure_argv.items[0] = try configure_exe_path.toString(arena); - - switch (cache_poison) { - .pure, .disallowed, .ignored => if (try config_man.hit()) { - const digest = config_man.final(); - break :cp .{ - .{ - .root_dir = graph.local_cache_root, - .sub_path = try allocPrint(arena, "c/{s}", .{&digest}), - }, - false, - }; - }, - .poisoned => {}, // Don't bother checking for cache hit. - } } if (!process.can_spawn) { -- 2.54.0 From b4b92c71e551f6fdbc08879efe3650816bed1e14 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 23 Jun 2026 23:50:35 -0700 Subject: [PATCH 14/49] std.zig.buildExeSubprocess: fix memory management of file system inputs --- lib/std/Build/Cache.zig | 2 ++ lib/std/zig.zig | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index b3283856d9e64339cbaf153601aa80a0265bcefb..54f0b8baad329214e94a37d56173f04d04c4d082 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -1007,6 +1007,8 @@ pub const Manifest = struct { keep = try addPrefixedPathPost(self, prefixed_path); } + /// Low level function. `prefixed_path` references cloned memory. Returns + /// whether or not `prefixed_path.sub_path` should be kept. pub fn addPrefixedPathPost(man: *Manifest, prefixed_path: PrefixedPath) !bool { assert(man.manifest_file != null); const gpa = man.cache.gpa; diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 6efe0cc7431e367f19add7b142db99641e6728b7..657b05638e03147e5d54d51b3768b97004333276 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -1754,8 +1754,10 @@ pub fn buildExeSubprocess(gpa: Allocator, io: Io, options: BuildExeSubprocessOpt var it = mem.splitScalar(u8, body, 0); while (it.next()) |prefixed_path| { const prefix: Server.Message.PathPrefix = @enumFromInt(prefixed_path[0] - 1); - const sub_path = prefixed_path[1..]; - _ = man.addPrefixedPathPost(.{ + const sub_path = try gpa.dupe(u8, prefixed_path[1..]); + var keep = false; + defer if (!keep) gpa.free(sub_path); + keep = man.addPrefixedPathPost(.{ .prefix = @intFromEnum(prefix), .sub_path = sub_path, }) catch |err| switch (err) { -- 2.54.0 From 15204cc210e4010bfbd033f81aebad5df38bcde8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 00:01:21 -0700 Subject: [PATCH 15/49] cmake: remove dead files --- CMakeLists.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3211a15d4ddeeb9fe0ffa834482aa4e8ad486621..893baf2951d7136d7b291e4fce5c1ac827167022 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -335,11 +335,6 @@ set(ZIG_STAGE2_SOURCES src/Compilation.zig src/Compilation/Config.zig src/InternPool.zig - src/Package.zig - src/Package/Fetch.zig - src/Package/Fetch/git.zig - src/Package/Manifest.zig - src/Package/Module.zig src/RangeSet.zig src/Sema.zig src/Sema/reinterpret.zig @@ -369,7 +364,6 @@ set(ZIG_STAGE2_SOURCES src/libs/glibc.zig src/libs/netbsd.zig src/libs/openbsd.zig - src/introspect.zig src/libs/libcxx.zig src/libs/libtsan.zig src/libs/libunwind.zig -- 2.54.0 From b384a7f85859678657f0ee91041be27078dfd346 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 00:08:53 -0700 Subject: [PATCH 16/49] compiler: import Module instead of Package it's almost like all these references to Package actually wanted to be references to Module instead... --- src/Builtin.zig | 2 +- src/Compilation.zig | 48 ++++++++++++++++----------------- src/Compilation/Config.zig | 2 +- src/Module.zig | 8 +++--- src/Sema.zig | 4 +-- src/Zcu.zig | 30 ++++++++++----------- src/Zcu/PerThread.zig | 2 +- src/codegen/aarch64/Select.zig | 4 +-- src/codegen/c.zig | 2 +- src/codegen/llvm.zig | 4 +-- src/codegen/llvm/FuncGen.zig | 4 +-- src/codegen/riscv64/CodeGen.zig | 4 +-- src/codegen/x86_64/CodeGen.zig | 2 +- src/libs/freebsd.zig | 2 +- src/libs/glibc.zig | 2 +- src/libs/libcxx.zig | 2 +- src/libs/libtsan.zig | 2 +- src/libs/libunwind.zig | 2 +- src/libs/musl.zig | 2 +- src/libs/netbsd.zig | 2 +- src/libs/openbsd.zig | 2 +- src/link.zig | 1 - src/link/C.zig | 2 +- src/link/Dwarf.zig | 2 +- src/main.zig | 29 ++++++++++---------- 25 files changed, 82 insertions(+), 84 deletions(-) diff --git a/src/Builtin.zig b/src/Builtin.zig index b45841d152aab73e281e3e7865b106a8539e6ce2..2117aae11b1ef834cfdb594dfc1f7ea2711e5406 100644 --- a/src/Builtin.zig +++ b/src/Builtin.zig @@ -370,7 +370,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const Cache = std.Build.Cache; const build_options = @import("build_options"); -const Module = @import("Package/Module.zig"); +const Module = @import("Module.zig"); const assert = std.debug.assert; const AstGen = std.zig.AstGen; const File = @import("Zcu.zig").File; diff --git a/src/Compilation.zig b/src/Compilation.zig index 55593959ab44b807ed69fa2f3cecfcbfe76542d5..da8844e0de453dd86ee07e2f2156d64cfaaf231f 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -16,7 +16,6 @@ const fatal = std.process.fatal; const Value = @import("Value.zig"); const Type = @import("Type.zig"); const target_util = @import("target.zig"); -const Package = @import("Package.zig"); const link = @import("link.zig"); const tracy = @import("tracy.zig"); const trace = tracy.trace; @@ -43,6 +42,7 @@ const Air = @import("Air.zig"); const Builtin = @import("Builtin.zig"); const LlvmObject = @import("codegen/llvm.zig").Object; const dev = @import("dev.zig"); +const Module = @import("Module.zig"); pub const Config = @import("Compilation/Config.zig"); @@ -63,7 +63,7 @@ cache_use: CacheUse, /// All compilations have a root module because this is where some important /// settings are stored, such as target and optimization mode. This module /// might not have any .zig code associated with it, however. -root_mod: *Package.Module, +root_mod: *Module, /// User-specified settings that have all the defaults resolved into concrete values. config: Config, @@ -766,7 +766,7 @@ pub const CrtFile = struct { /// For passing to a C compiler. pub const CSourceFile = struct { /// Many C compiler flags are determined by settings contained in the owning Module. - owner: *Package.Module, + owner: *Module, src_path: []const u8, extra_flags: []const []const u8 = &.{}, /// Same as extra_flags except they are not added to the Cache hash. @@ -778,7 +778,7 @@ pub const CSourceFile = struct { /// For passing to resinator. pub const RcSourceFile = struct { - owner: *Package.Module, + owner: *Module, src_path: []const u8, extra_flags: []const []const u8 = &.{}, }; @@ -1216,7 +1216,7 @@ pub const MiscError = struct { }; pub const cache_helpers = struct { - pub fn addModule(hh: *Cache.HashHelper, mod: *const Package.Module) void { + pub fn addModule(hh: *Cache.HashHelper, mod: *const Module) void { addResolvedTarget(hh, mod.resolved_target); hh.add(mod.optimize_mode); hh.add(mod.code_model); @@ -1239,7 +1239,7 @@ pub const cache_helpers = struct { pub fn addResolvedTarget( hh: *Cache.HashHelper, - resolved_target: Package.Module.ResolvedTarget, + resolved_target: Module.ResolvedTarget, ) void { const target = &resolved_target.result; hh.add(target.cpu.arch); @@ -1412,16 +1412,16 @@ pub const CreateOptions = struct { /// Options that have been resolved by calling `resolveDefaults`. config: Compilation.Config, - root_mod: *Package.Module, + root_mod: *Module, /// Normally, `main_mod` and `root_mod` are the same. The exception is `zig /// test`, in which `root_mod` is the test runner, and `main_mod` is the /// user's source file which has the tests. - main_mod: ?*Package.Module = null, + main_mod: ?*Module = null, /// This is provided so that the API user has a chance to tweak the /// per-module settings of the standard library. /// When this is null, a default configuration of the std lib is created /// based on the settings of root_mod. - std_mod: ?*Package.Module = null, + std_mod: ?*Module = null, root_name: []const u8, sysroot: ?[]const u8 = null, cache_mode: CacheMode, @@ -1763,7 +1763,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, if (compiler_rt_strat == .zcu) { // For objects, this mechanism relies on essentially `_ = @import("compiler-rt");` // injected into the object. - const compiler_rt_mod = Package.Module.create(arena, .{ + const compiler_rt_mod = Module.create(arena, .{ .paths = .{ .root = .zig_lib_root, .root_src_path = "compiler_rt.zig", @@ -1825,7 +1825,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, }; if (ubsan_rt_strat == .zcu) { - const ubsan_rt_mod = Package.Module.create(arena, .{ + const ubsan_rt_mod = Module.create(arena, .{ .paths = .{ .root = .zig_lib_root, .root_src_path = "ubsan_rt.zig", @@ -1866,7 +1866,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, }; if (zigc_strat == .zcu) { - const zigc_mod = Package.Module.create(arena, .{ + const zigc_mod = Module.create(arena, .{ .paths = .{ .root = .zig_lib_root, .root_src_path = "c.zig", @@ -1996,7 +1996,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, .path = try options.dirs.global_cache.join(arena, &.{zir_sub_dir}), }; - const std_mod = options.std_mod orelse Package.Module.create(arena, .{ + const std_mod = options.std_mod orelse Module.create(arena, .{ .paths = .{ .root = try .fromRoot(arena, options.dirs, .zig_lib, "std"), .root_src_path = "std.zig", @@ -4639,7 +4639,7 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void { var buffer: [1024]u8 = undefined; var tar_file_writer = tar_file.writer(io, &buffer); - var seen_table: std.array_hash_map.Auto(*Package.Module, []const u8) = .empty; + var seen_table: std.array_hash_map.Auto(*Module, []const u8) = .empty; defer seen_table.deinit(comp.gpa); try seen_table.put(comp.gpa, zcu.main_mod, comp.root_name); @@ -4666,7 +4666,7 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void { fn docsCopyModule( comp: *Compilation, - module: *Package.Module, + module: *Module, name: []const u8, tar_file_writer: *Io.File.Writer, ) !void { @@ -4746,7 +4746,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubU const optimize_mode = std.lang.OptimizeMode.ReleaseSmall; const output_mode = std.lang.OutputMode.Exe; - const resolved_target: Package.Module.ResolvedTarget = .{ + const resolved_target: Module.ResolvedTarget = .{ .result = std.zig.system.resolveTargetQuery(io, .{ .cpu_arch = .wasm32, .os_tag = .freestanding, @@ -4786,7 +4786,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubU const dirs = comp.dirs.withoutLocalCache(); - const root_mod = Package.Module.create(arena, .{ + const root_mod = Module.create(arena, .{ .paths = .{ .root = try .fromRoot(arena, dirs, .zig_lib, "docs/wasm"), .root_src_path = src_basename, @@ -4803,7 +4803,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubU comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: failed to create root module: {t}", .{err}); return error.AlreadyReported; }; - const walk_mod = Package.Module.create(arena, .{ + const walk_mod = Module.create(arena, .{ .paths = .{ .root = try .fromRoot(arena, dirs, .zig_lib, "docs/wasm"), .root_src_path = "Walk.zig", @@ -4888,7 +4888,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubU pub fn obtainCObjectCacheManifest( comp: *const Compilation, - owner_mod: *Package.Module, + owner_mod: *Module, ) Cache.Manifest { var man = comp.cache_parent.obtain(); @@ -4936,7 +4936,7 @@ pub fn translateC( ext: FileExt, source_path: []const u8, translated_basename: []const u8, - owner_mod: *Package.Module, + owner_mod: *Module, prog_node: std.Progress.Node, environ_map: *const std.process.Environ.Map, ) !TranslateCResult { @@ -6092,7 +6092,7 @@ fn addCommonCCArgs( argv: *std.array_list.Managed([]const u8), ext: FileExt, out_dep_path: ?[]const u8, - mod: *Package.Module, + mod: *Module, c_frontend: Config.CFrontend, ) !void { const target = &mod.resolved_target.result; @@ -6446,7 +6446,7 @@ pub fn addCCArgs( argv: *std.array_list.Managed([]const u8), ext: FileExt, out_dep_path: ?[]const u8, - mod: *Package.Module, + mod: *Module, ) !void { const target = &mod.resolved_target.result; @@ -7237,7 +7237,7 @@ fn buildOutputFromZig( return error.AlreadyReported; }; - const root_mod = Package.Module.create(arena, .{ + const root_mod = Module.create(arena, .{ .paths = .{ .root = .zig_lib_root, .root_src_path = src_basename, @@ -7384,7 +7384,7 @@ pub fn build_crt_file( comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: failed to resolve compilation config: {t}", .{ misc_task_tag, err }); return error.AlreadyReported; }; - const root_mod = Package.Module.create(arena, .{ + const root_mod = Module.create(arena, .{ .paths = .{ .root = .zig_lib_root, .root_src_path = "", diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig index ce180f9ead8c4f545f4e5e8ea2a6ecc3c7b33179..9dd28b3e37b3cfbfa247dde25368b5b59ed530e2 100644 --- a/src/Compilation/Config.zig +++ b/src/Compilation/Config.zig @@ -577,7 +577,7 @@ pub fn resolve(options: Options) ResolveError!Config { } const std = @import("std"); -const Module = @import("../Package.zig").Module; +const Module = @import("../Module.zig"); const Config = @This(); const target_util = @import("../target.zig"); const build_options = @import("build_options"); diff --git a/src/Module.zig b/src/Module.zig index 02c65b09fcb13eb9ead45c4e9ce925d01b627e43..ff821831ef72aead6ea30c27b6f826e74875e507 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -6,10 +6,10 @@ const Allocator = std.mem.Allocator; const Cache = std.Build.Cache; const assert = std.debug.assert; -const target_util = @import("../target.zig"); -const Builtin = @import("../Builtin.zig"); -const Compilation = @import("../Compilation.zig"); -const File = @import("../Zcu.zig").File; +const target_util = @import("target.zig"); +const Builtin = @import("Builtin.zig"); +const Compilation = @import("Compilation.zig"); +const File = @import("Zcu.zig").File; /// The root directory of the module. Only files inside this directory can be imported. root: Compilation.Path, diff --git a/src/Sema.zig b/src/Sema.zig index 2469444831e1037c7e526a33a0f4c2f39b26474a..2c1d69b23e071c3c2bdaff4a19ed9f5ba0684daa 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -25,7 +25,6 @@ const SemaError = Zcu.SemaError; const LazySrcLoc = Zcu.LazySrcLoc; const RangeSet = @import("RangeSet.zig"); const target_util = @import("target.zig"); -const Package = @import("Package.zig"); const crash_report = @import("crash_report.zig"); const build_options = @import("build_options"); const Compilation = @import("Compilation.zig"); @@ -36,6 +35,7 @@ const ComptimeAllocIndex = InternPool.ComptimeAllocIndex; const Cache = std.Build.Cache; const LowerZon = @import("Sema/LowerZon.zig"); const arith = @import("Sema/arith.zig"); +const Module = @import("Module.zig"); pt: Zcu.PerThread, /// Alias to `zcu.gpa`. @@ -839,7 +839,7 @@ pub const Block = struct { return result_index; } - pub fn ownerModule(block: Block) *Package.Module { + pub fn ownerModule(block: Block) *Module { const zcu = block.sema.pt.zcu; return zcu.namespacePtr(block.namespace).fileScope(zcu).mod.?; } diff --git a/src/Zcu.zig b/src/Zcu.zig index 41758eaf3c3b2ae6ef9ce3adfef9ccb734bda5f4..d36c96c07dab3f38df7abfff2a67003529a80201 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -25,7 +25,7 @@ const Compilation = @import("Compilation.zig"); const Cache = std.Build.Cache; pub const Value = @import("Value.zig"); pub const Type = @import("Type.zig"); -const Package = @import("Package.zig"); +const Module = @import("Module.zig"); const link = @import("link.zig"); const Air = @import("Air.zig"); const Zir = std.zig.Zir; @@ -64,11 +64,11 @@ comp: *Compilation, llvm_object: ?LlvmObject.Ptr, /// Pointer to externally managed resource. -root_mod: *Package.Module, +root_mod: *Module, /// Normally, `main_mod` and `root_mod` are the same. The exception is `zig test`, in which /// `root_mod` is the test runner, and `main_mod` is the user's source file which has the tests. -main_mod: *Package.Module, -std_mod: *Package.Module, +main_mod: *Module, +std_mod: *Module, sema_prog_node: std.Progress.Node = .none, codegen_prog_node: std.Progress.Node = .none, /// The number of codegen jobs which are pending or in-progress. Whichever thread drops this value @@ -105,11 +105,11 @@ multi_exports: std.array_hash_map.Auto(AnalUnit, extern struct { }) = .{}, /// Key is the digest returned by `Builtin.hash`; value is the corresponding module. -builtin_modules: std.array_hash_map.Auto(Cache.BinDigest, *Package.Module) = .empty, +builtin_modules: std.array_hash_map.Auto(Cache.BinDigest, *Module) = .empty, /// Populated as soon as the `Compilation` is created. Guaranteed to contain all modules, even builtin ones. /// Modules whose root file is not a Zig or ZON file have the value `.none`. -module_roots: std.array_hash_map.Auto(*Package.Module, File.Index.Optional) = .empty, +module_roots: std.array_hash_map.Auto(*Module, File.Index.Optional) = .empty, /// The set of all the Zig source files in the Zig Compilation Unit. Tracked in /// order to iterate over it and check which source files have been modified on @@ -148,7 +148,7 @@ alive_files: std.array_hash_map.Auto(File.Index, File.Reference) = .empty, /// Cleared and recomputed every update, after AstGen and before Sema. multi_module_err: ?struct { file: File.Index, - modules: [2]*Package.Module, + modules: [2]*Module, refs: [2]File.Reference, } = null, @@ -292,7 +292,7 @@ retryable_failures: std.ArrayList(AnalUnit) = .empty, /// These are the modules which we initially queue for analysis in `Compilation.update`. /// `resolveReferences` will use these as the root of its reachability traversal. -analysis_roots_buffer: [5]*Package.Module, +analysis_roots_buffer: [5]*Module, analysis_roots_len: usize = 0, /// This is the cached result of `Zcu.resolveReferences`. It is computed on-demand, and /// reset to `null` when any semantic analysis occurs (since this invalidates the data). @@ -985,7 +985,7 @@ pub const File = struct { /// tell, and invalidate dependencies as needed (see `module_changed`). /// During semantic analysis, this is always non-`null` for alive files (i.e. those which /// have imports targeting them). - mod: ?*Package.Module, + mod: ?*Module, /// Relative to the root directory of `mod`. If `mod == null`, this field is `undefined`. /// This memory is managed externally and must not be directly freed. /// Its lifetime is at least equal to that of this `File`. @@ -1028,13 +1028,13 @@ pub const File = struct { /// A single reference to a file. pub const Reference = union(enum) { - analysis_root: *Package.Module, + analysis_root: *Module, import: struct { importer: Zcu.File.Index, tok: Ast.TokenIndex, /// If the file is imported as the root of a module, this is that module. /// `null` means the file was imported directly by path. - module: ?*Package.Module, + module: ?*Module, }, }; @@ -3709,7 +3709,7 @@ pub const ImportResult = struct { /// If this import was a simple file path, this is `null`; the imported file should exist within /// the importer's module. Otherwise, it's the module which the import resolved to. This module /// could match the module of `cur_file`, since a module can depend on itself. - module: ?*Package.Module, + module: ?*Module, }; /// Prepares `unit` for re-analysis by clearing all of the following state: @@ -4406,7 +4406,7 @@ fn resolveReferencesInner(zcu: *Zcu) Allocator.Error!std.array_hash_map.Auto(Ana return units.move(); } -pub fn analysisRoots(zcu: *Zcu) []*Package.Module { +pub fn analysisRoots(zcu: *Zcu) []*Module { return zcu.analysis_roots_buffer[0..zcu.analysis_roots_len]; } @@ -4820,7 +4820,7 @@ fn explainWhyFileIsInModule( eb: *std.zig.ErrorBundle.Wip, notes_out: *std.ArrayList(std.zig.ErrorBundle.MessageIndex), file: File.Index, - in_module: *Package.Module, + in_module: *Module, ref: File.Reference, ) Allocator.Error!void { const gpa = zcu.gpa; @@ -4866,7 +4866,7 @@ fn explainWhyFileIsInModule( const import_src = try importer_file.errorBundleTokenSrc(import.tok, zcu, eb); const importer_ref = zcu.alive_files.get(import.importer).?; - const importer_root: ?*Package.Module = switch (importer_ref) { + const importer_root: ?*Module = switch (importer_ref) { .analysis_root => |mod| mod, .import => |i| i.module, }; diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index f9aede4f72b4c0a4e32b06f02ac509ef62d30d09..c6c665655cf1ae7a792fabb44b5433efa6664bef 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -23,7 +23,7 @@ const builtin = @import("builtin"); const dev = @import("../dev.zig"); const InternPool = @import("../InternPool.zig"); const AnalUnit = InternPool.AnalUnit; -const Module = @import("../Package.zig").Module; +const Module = @import("../Module.zig"); const Sema = @import("../Sema.zig"); const target_util = @import("../target.zig"); const tracy = @import("../tracy.zig"); diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig index c8cfd0aeda8a5a51594d278926e3dd8666e57e83..ba8cde4e9c45bbdca010d1d87eb1e8a2b3ef2f52 100644 --- a/src/codegen/aarch64/Select.zig +++ b/src/codegen/aarch64/Select.zig @@ -7592,7 +7592,7 @@ pub fn layout( is_sysv_var_args: bool, saved_gra_len: u7, saved_vra_len: u7, - mod: *const Package.Module, + mod: *const Module, ) !usize { const zcu = isel.pt.zcu; const ip = &zcu.intern_pool; @@ -12513,7 +12513,7 @@ const assert = std.debug.assert; const codegen = @import("../../codegen.zig"); const Constant = @import("../../Value.zig"); const InternPool = @import("../../InternPool.zig"); -const Package = @import("../../Package.zig"); +const Module = @import("../../Module.zig"); const Register = codegen.aarch64.encoding.Register; const Select = @This(); const std = @import("std"); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 84744bf3cc55e62603b2f01cff265539979f23f0..b814fe55c16b3c96ab7f6caee7b4e1119ae73257 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -9,7 +9,7 @@ const Writer = std.Io.Writer; const dev = @import("../dev.zig"); const link = @import("../link.zig"); const Zcu = @import("../Zcu.zig"); -const Module = @import("../Package/Module.zig"); +const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); const Value = @import("../Value.zig"); const Type = @import("../Type.zig"); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6361963ab4d898bcf84a08fed641c7390e476e4a..2e79acc1e3e930b8c437099650329117a61e5428 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -13,7 +13,7 @@ const Compilation = @import("../Compilation.zig"); const dev = @import("../dev.zig"); const InternPool = @import("../InternPool.zig"); const link = @import("../link.zig"); -const Package = @import("../Package.zig"); +const Module = @import("../Module.zig"); const target_util = @import("../target.zig"); const Type = @import("../Type.zig"); const Value = @import("../Value.zig"); @@ -2772,7 +2772,7 @@ pub const Object = struct { fn addCommonFnAttributes( o: *Object, attributes: *Builder.FunctionAttributes.Wip, - owner_mod: *Package.Module, + owner_mod: *Module, omit_frame_pointer: bool, ) Allocator.Error!void { if (!owner_mod.red_zone) { diff --git a/src/codegen/llvm/FuncGen.zig b/src/codegen/llvm/FuncGen.zig index 25572de79a872b0d2f5fa51ef4ca16ca762514e4..bad4b1461b1c01274d481c984a752bb6704d4615 100644 --- a/src/codegen/llvm/FuncGen.zig +++ b/src/codegen/llvm/FuncGen.zig @@ -80,7 +80,7 @@ fn todo(fg: *FuncGen, comptime format: []const u8, args: anytype) TodoError { ); } -fn ownerModule(fg: *const FuncGen) *Package.Module { +fn ownerModule(fg: *const FuncGen) *Module { return fg.object.zcu.navFileScope(fg.nav_index).mod.?; } @@ -7739,7 +7739,7 @@ const mips_c_abi = @import("../mips/abi.zig"); const Zcu = @import("../../Zcu.zig"); const Air = @import("../../Air.zig"); -const Package = @import("../../Package.zig"); +const Module = @import("../../Module.zig"); const InternPool = @import("../../InternPool.zig"); const Value = @import("../../Value.zig"); const Type = @import("../../Type.zig"); diff --git a/src/codegen/riscv64/CodeGen.zig b/src/codegen/riscv64/CodeGen.zig index 08df78513efa32c24a4dab639d2d3f9878689153..4bd30651254a7582d1d49a82562526a9c3e44735 100644 --- a/src/codegen/riscv64/CodeGen.zig +++ b/src/codegen/riscv64/CodeGen.zig @@ -14,7 +14,7 @@ const Type = @import("../../Type.zig"); const Value = @import("../../Value.zig"); const link = @import("../../link.zig"); const Zcu = @import("../../Zcu.zig"); -const Package = @import("../../Package.zig"); +const Module = @import("../../Module.zig"); const InternPool = @import("../../InternPool.zig"); const Compilation = @import("../../Compilation.zig"); const target_util = @import("../../target.zig"); @@ -66,7 +66,7 @@ liveness: Air.Liveness, bin_file: *link.File, gpa: Allocator, -mod: *Package.Module, +mod: *Module, target: *const std.Target, args: []MCValue, ret_mcv: InstTracking, diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig index 5ff7a45954737fdaf651d1073212ec27fa08ddb5..74fb9b4f87725574506a14178d989a66b5ae5731 100644 --- a/src/codegen/x86_64/CodeGen.zig +++ b/src/codegen/x86_64/CodeGen.zig @@ -14,7 +14,7 @@ const Emit = @import("Emit.zig"); const Lower = @import("Lower.zig"); const Mir = @import("Mir.zig"); const Zcu = @import("../../Zcu.zig"); -const Module = @import("../../Package/Module.zig"); +const Module = @import("../../Module.zig"); const InternPool = @import("../../InternPool.zig"); const Type = @import("../../Type.zig"); const Value = @import("../../Value.zig"); diff --git a/src/libs/freebsd.zig b/src/libs/freebsd.zig index 2270de6d60fd38aa527c9bd78ee21823e2b89554..ecc15e0dab899e21093c047e58a7b02467a29ac6 100644 --- a/src/libs/freebsd.zig +++ b/src/libs/freebsd.zig @@ -12,7 +12,7 @@ const Compilation = @import("../Compilation.zig"); const build_options = @import("build_options"); const trace = @import("../tracy.zig").trace; const Cache = std.Build.Cache; -const Module = @import("../Package/Module.zig"); +const Module = @import("../Module.zig"); const link = @import("../link.zig"); pub const CrtFile = enum { diff --git a/src/libs/glibc.zig b/src/libs/glibc.zig index 9d07149dfc867f68640a73f34e3e9d19656fff26..af80133d3332e33b69334b6c2b3dcc1b3f075979 100644 --- a/src/libs/glibc.zig +++ b/src/libs/glibc.zig @@ -12,7 +12,7 @@ const Compilation = @import("../Compilation.zig"); const build_options = @import("build_options"); const trace = @import("../tracy.zig").trace; const Cache = std.Build.Cache; -const Module = @import("../Package/Module.zig"); +const Module = @import("../Module.zig"); const link = @import("../link.zig"); pub const Lib = struct { diff --git a/src/libs/libcxx.zig b/src/libs/libcxx.zig index 3eb4161235382c9b63dab07edf88c27e74dc1a08..cc91db8333dde6f83f32e7f964742d36e692cc92 100644 --- a/src/libs/libcxx.zig +++ b/src/libs/libcxx.zig @@ -6,7 +6,7 @@ const target_util = @import("../target.zig"); const Compilation = @import("../Compilation.zig"); const build_options = @import("build_options"); const trace = @import("../tracy.zig").trace; -const Module = @import("../Package/Module.zig"); +const Module = @import("../Module.zig"); const libcxxabi_files = [_][]const u8{ "src/cxa_aux_runtime.cpp", diff --git a/src/libs/libtsan.zig b/src/libs/libtsan.zig index 6f83d106455905eb17d0c19adff84b295fb5e8ed..af8830699ca5d240ea379d98cb9382d67fe0d984 100644 --- a/src/libs/libtsan.zig +++ b/src/libs/libtsan.zig @@ -4,7 +4,7 @@ const assert = std.debug.assert; const Compilation = @import("../Compilation.zig"); const build_options = @import("build_options"); const trace = @import("../tracy.zig").trace; -const Module = @import("../Package/Module.zig"); +const Module = @import("../Module.zig"); pub const BuildError = error{ OutOfMemory, diff --git a/src/libs/libunwind.zig b/src/libs/libunwind.zig index e4c1096efd5d37368d485100be96304d77be2846..82ef9c34bc3d76b5cdbf2306ff8faf2526f54cbb 100644 --- a/src/libs/libunwind.zig +++ b/src/libs/libunwind.zig @@ -4,7 +4,7 @@ const assert = std.debug.assert; const target_util = @import("../target.zig"); const Compilation = @import("../Compilation.zig"); -const Module = @import("../Package/Module.zig"); +const Module = @import("../Module.zig"); const build_options = @import("build_options"); const trace = @import("../tracy.zig").trace; diff --git a/src/libs/musl.zig b/src/libs/musl.zig index fcf179894c6d6f0d542f04fb925788c76f82ca1a..e3613d020b203a9dc9858bb8c48d1316a2338509 100644 --- a/src/libs/musl.zig +++ b/src/libs/musl.zig @@ -3,7 +3,7 @@ const Allocator = std.mem.Allocator; const mem = std.mem; const path = std.fs.path; const assert = std.debug.assert; -const Module = @import("../Package/Module.zig"); +const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); const build_options = @import("build_options"); diff --git a/src/libs/netbsd.zig b/src/libs/netbsd.zig index 15462afd3db224ee6fde38ce6737014a72d89c3c..c3e0a38ffb6cd6cb98cfd51bb0cc74151c627bd4 100644 --- a/src/libs/netbsd.zig +++ b/src/libs/netbsd.zig @@ -12,7 +12,7 @@ const Compilation = @import("../Compilation.zig"); const build_options = @import("build_options"); const trace = @import("../tracy.zig").trace; const Cache = std.Build.Cache; -const Module = @import("../Package/Module.zig"); +const Module = @import("../Module.zig"); const link = @import("../link.zig"); pub const CrtFile = enum { diff --git a/src/libs/openbsd.zig b/src/libs/openbsd.zig index 05d30e53edf9130ea13e32d6f42e40870c00198d..ee50196c8169bcb52f0a1bbd455f4b14c20f93e8 100644 --- a/src/libs/openbsd.zig +++ b/src/libs/openbsd.zig @@ -13,7 +13,7 @@ const Compilation = @import("../Compilation.zig"); const build_options = @import("build_options"); const trace = @import("../tracy.zig").trace; const Cache = std.Build.Cache; -const Module = @import("../Package/Module.zig"); +const Module = @import("../Module.zig"); const link = @import("../link.zig"); pub const CrtFile = enum { diff --git a/src/link.zig b/src/link.zig index 4764e0c291f4c048812f5f30dc94d1f948c9b442..5307868ddf691e8df3f9b1912ddb63649897d0d4 100644 --- a/src/link.zig +++ b/src/link.zig @@ -21,7 +21,6 @@ const Zcu = @import("Zcu.zig"); const InternPool = @import("InternPool.zig"); const Type = @import("Type.zig"); const Value = @import("Value.zig"); -const Package = @import("Package.zig"); const dev = @import("dev.zig"); const target_util = @import("target.zig"); const codegen = @import("codegen.zig"); diff --git a/src/link/C.zig b/src/link/C.zig index 127485171314adc352096f6e13b448b28d12e173..a5862d87eeba5b8833abcc92021e6f1ee74318a1 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -13,7 +13,7 @@ const Path = std.Build.Cache.Path; const build_options = @import("build_options"); const Zcu = @import("../Zcu.zig"); -const Module = @import("../Package/Module.zig"); +const Module = @import("../Module.zig"); const InternPool = @import("../InternPool.zig"); const Alignment = InternPool.Alignment; const Compilation = @import("../Compilation.zig"); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 96fb3d10c9191de8a2acaf7acaf799f5b9832fc5..d51c59ed23941283b2de8258db0ad865adf8aa33 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -10,7 +10,7 @@ const log = std.log.scoped(.dwarf); const Writer = std.Io.Writer; const InternPool = @import("../InternPool.zig"); -const Module = @import("../Package.zig").Module; +const Module = @import("../Module.zig"); const Type = @import("../Type.zig"); const Value = @import("../Value.zig"); const Zcu = @import("../Zcu.zig"); diff --git a/src/main.zig b/src/main.zig index 9d088946bcfeb147da456cb983953935a1c3c0aa..72d652ee3b8daccbe534f5856d4bdedd98615cd3 100644 --- a/src/main.zig +++ b/src/main.zig @@ -26,7 +26,6 @@ const allocPrint = std.fmt.allocPrint; pub const tracy = @import("tracy.zig"); const Compilation = @import("Compilation.zig"); const link = @import("link.zig"); -const Package = @import("Package.zig"); const build_options = @import("build_options"); const wasi_libc = @import("libs/wasi_libc.zig"); const target_util = @import("target.zig"); @@ -34,9 +33,9 @@ const crash_report = @import("crash_report.zig"); const Zcu = @import("Zcu.zig"); const mingw = @import("libs/mingw.zig"); const dev = @import("dev.zig"); +const Module = @import("Module.zig"); test { - _ = Package; _ = @import("codegen.zig"); } @@ -878,13 +877,13 @@ const CliModule = struct { root_path: []const u8, root_src_path: []const u8, cc_argv: []const []const u8, - inherited: Package.Module.CreateOptions.Inherited, + inherited: Module.CreateOptions.Inherited, target_arch_os_abi: ?[]const u8, target_mcpu: ?[]const u8, dynamic_linker: ?[]const u8, deps: []const Dep, - resolved: ?*Package.Module, + resolved: ?*Module, c_source_files_start: usize, c_source_files_end: usize, @@ -1035,7 +1034,7 @@ fn buildOutputType( // These get set by CLI flags and then snapshotted when a `-M` flag is // encountered. - var mod_opts: Package.Module.CreateOptions.Inherited = .{}; + var mod_opts: Module.CreateOptions.Inherited = .{}; // These get appended to by CLI flags and then slurped when a `-M` flag // is encountered. @@ -3266,7 +3265,7 @@ fn buildOutputType( const root_mod = switch (arg_mode) { .zig_test, .zig_test_obj => root_mod: { const test_mod = if (test_runner_path) |test_runner| test_mod: { - const test_mod = try Package.Module.create(arena, .{ + const test_mod = try Module.create(arena, .{ .paths = .{ .root = try .fromUnresolved(arena, dirs, &.{fs.path.dirname(test_runner) orelse "."}), .root_src_path = fs.path.basename(test_runner), @@ -3279,7 +3278,7 @@ fn buildOutputType( }); test_mod.deps = try main_mod.deps.clone(arena); break :test_mod test_mod; - } else try Package.Module.create(arena, .{ + } else try Module.create(arena, .{ .paths = .{ .root = try .fromRoot(arena, dirs, .zig_lib, "compiler"), .root_src_path = "test_runner.zig", @@ -3973,10 +3972,10 @@ fn createModule( io: Io, create_module: *CreateModule, index: usize, - parent: ?*Package.Module, + parent: ?*Module, color: std.zig.Color, environ_map: *process.Environ.Map, -) Allocator.Error!*Package.Module { +) Allocator.Error!*Module { const cli_mod = &create_module.modules.values()[index]; if (cli_mod.resolved) |m| return m; @@ -4252,7 +4251,7 @@ fn createModule( const root: Compilation.Path = try .fromUnresolved(arena, create_module.dirs, &.{cli_mod.root_path}); - const mod = Package.Module.create(arena, .{ + const mod = Module.create(arena, .{ .paths = .{ .root = root, .root_src_path = cli_mod.root_src_path, @@ -4919,7 +4918,7 @@ fn jitCmdInner( options: JitCmdOptions, ) !void { const target_query: std.Target.Query = .{}; - const resolved_target: Package.Module.ResolvedTarget = .{ + const resolved_target: Module.ResolvedTarget = .{ .result = std.zig.resolveTargetQueryOrFatal(io, target_query), .is_native_os = true, .is_native_abi = true, @@ -4959,7 +4958,7 @@ fn jitCmdInner( // We want to release all the locks before executing the child process, so we make a nice // big block here to ensure the cleanup gets run when we extract out our argv. { - const main_mod_paths: Package.Module.CreateOptions.Paths = .{ + const main_mod_paths: Module.CreateOptions.Paths = .{ .root = try .fromRoot(arena, dirs, .zig_lib, "compiler"), .root_src_path = options.root_src_path, }; @@ -4974,7 +4973,7 @@ fn jitCmdInner( .is_test = false, }); - const root_mod = try Package.Module.create(arena, .{ + const root_mod = try Module.create(arena, .{ .paths = main_mod_paths, .fully_qualified_name = "root", .cc_argv = &.{}, @@ -4988,7 +4987,7 @@ fn jitCmdInner( }); if (options.depend_on_aro) { - const aro_mod = try Package.Module.create(arena, .{ + const aro_mod = try Module.create(arena, .{ .paths = .{ .root = try .fromRoot(arena, dirs, .zig_lib, "compiler/aro"), .root_src_path = "aro.zig", @@ -6023,7 +6022,7 @@ fn handleModArg( mod_name: []const u8, opt_root_src_orig: ?[]const u8, create_module: *CreateModule, - mod_opts: *Package.Module.CreateOptions.Inherited, + mod_opts: *Module.CreateOptions.Inherited, cc_argv: *std.ArrayList([]const u8), target_arch_os_abi: *?[]const u8, target_mcpu: *?[]const u8, -- 2.54.0 From d5c1e6332724b9cbd692ef38ff329b6814ed63b9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 00:11:09 -0700 Subject: [PATCH 17/49] compiler: fix references to Compilation.Directories --- src/Compilation.zig | 8 ++++---- src/Module.zig | 2 +- src/main.zig | 6 +++--- src/print_env.zig | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index da8844e0de453dd86ee07e2f2156d64cfaaf231f..3bdc049c3e3ba307a1430027567d50e39b346e1f 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -490,7 +490,7 @@ pub const Path = struct { /// From an unresolved path (which can be made of multiple not-yet-joined strings), construct a /// canonical `Path`. - pub fn fromUnresolved(gpa: Allocator, dirs: Compilation.Directories, unresolved_parts: []const []const u8) Allocator.Error!Path { + pub fn fromUnresolved(gpa: Allocator, dirs: std.zig.Directories, unresolved_parts: []const []const u8) Allocator.Error!Path { const resolved = try std.zig.resolvePath(gpa, dirs.cwd, unresolved_parts); errdefer gpa.free(resolved); @@ -565,7 +565,7 @@ pub const Path = struct { /// `.global_cache` could still end up returning a `Path` with `Path.root == .zig_lib`. pub fn fromRoot( gpa: Allocator, - dirs: Compilation.Directories, + dirs: std.zig.Directories, root: Path.Root, sub_path: []const u8, ) Allocator.Error!Path { @@ -588,7 +588,7 @@ pub const Path = struct { pub fn join( p: Path, gpa: Allocator, - dirs: Compilation.Directories, + dirs: std.zig.Directories, sub_path: []const u8, ) Allocator.Error!Path { // Currently, this just wraps `fromUnresolved` for simplicity. A more efficient impl is @@ -609,7 +609,7 @@ pub const Path = struct { pub fn upJoin( p: Path, gpa: Allocator, - dirs: Compilation.Directories, + dirs: std.zig.Directories, sub_path: []const u8, ) Allocator.Error!Path { return .fromUnresolved(gpa, dirs, &.{ diff --git a/src/Module.zig b/src/Module.zig index ff821831ef72aead6ea30c27b6f826e74875e507..b0d212070bd298f6d4980eddb4fe32a9fd43b363 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -457,7 +457,7 @@ pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*M } /// Does not ensure that the module's root directory exists on-disk; see `Builtin.updateFileOnDisk` for that task. -pub fn createBuiltin(arena: Allocator, opts: Builtin, dirs: Compilation.Directories) Allocator.Error!*Module { +pub fn createBuiltin(arena: Allocator, opts: Builtin, dirs: std.zig.Directories) Allocator.Error!*Module { const sub_path = "b" ++ std.fs.path.sep_str ++ Cache.binToHex(opts.hash()); const new = try arena.create(Module); new.* = .{ diff --git a/src/main.zig b/src/main.zig index 72d652ee3b8daccbe534f5856d4bdedd98615cd3..6402533471fca87cebdb5ae046d80aeb35601cf8 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3218,7 +3218,7 @@ fn buildOutputType( const cwd_path = try std.zig.getResolvedCwd(io, arena); // This `init` calls `fatal` on error. - var dirs: Compilation.Directories = .init( + var dirs: std.zig.Directories = .init( arena, io, override_lib_dir, @@ -3928,7 +3928,7 @@ fn buildOutputType( } const CreateModule = struct { - dirs: Compilation.Directories, + dirs: std.zig.Directories, modules: std.array_hash_map.String(CliModule), opts: Compilation.Config.Options, object_format: ?[]const u8, @@ -4939,7 +4939,7 @@ fn jitCmdInner( const cwd_path = try std.zig.getResolvedCwd(io, arena); // This `init` calls `fatal` on error. - var dirs: Compilation.Directories = .init( + var dirs: std.zig.Directories = .init( arena, io, override_lib_dir, diff --git a/src/print_env.zig b/src/print_env.zig index 93e14781a184db46288b9c5e8c3d77e598820f2c..34264234ad234faf38bf5bd744e67b833899ce7d 100644 --- a/src/print_env.zig +++ b/src/print_env.zig @@ -30,7 +30,7 @@ pub fn cmdEnv( const cwd_path = try std.zig.getResolvedCwd(io, arena); - var dirs: Compilation.Directories = .init( + var dirs: std.zig.Directories = .init( arena, io, override_lib_dir, -- 2.54.0 From 4eace8f68d49d4c0d2d0bbb8afad00aa79e8bbb5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 00:26:06 -0700 Subject: [PATCH 18/49] compiler: jitCmd optionally passes cmd name --- src/main.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index 6402533471fca87cebdb5ae046d80aeb35601cf8..acfb05675a19230b70f1b2ddaf29701a1856b59f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -356,6 +356,7 @@ fn mainArgs( return jitCmd(gpa, arena, io, args, environ_map, .{ .cmd_name = "maker", .root_src_path = "Maker.zig", + .prepend_cmd = cmd, .prepend_zig_lib_dir_path = true, .prepend_global_cache_path = true, .prepend_zig_exe_path = true, @@ -4871,6 +4872,7 @@ pub fn translateC( const JitCmdOptions = struct { cmd_name: []const u8, root_src_path: []const u8, + prepend_cmd: ?[]const u8 = null, prepend_zig_lib_dir_path: bool = false, prepend_global_cache_path: bool = false, prepend_zig_exe_path: bool = false, @@ -4953,7 +4955,7 @@ fn jitCmdInner( defer dirs.deinit(io); var child_argv: std.ArrayList([]const u8) = .empty; - try child_argv.ensureUnusedCapacity(arena, args.len + 5); + try child_argv.ensureUnusedCapacity(arena, args.len + 6); // We want to release all the locks before executing the child process, so we make a nice // big block here to ensure the cleanup gets run when we extract out our argv. @@ -5054,6 +5056,8 @@ fn jitCmdInner( child_argv.appendAssumeCapacity(exe_path); } + if (options.prepend_cmd) |cmd| + child_argv.appendAssumeCapacity(cmd); if (options.prepend_zig_lib_dir_path) child_argv.appendAssumeCapacity(try allocPrint(arena, "--zig-lib={s}", .{dirs.zig_lib.path.?})); if (options.prepend_zig_exe_path) -- 2.54.0 From 92e6c724e4d768c85598abf4d2363ab9937dfddc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 01:21:20 -0700 Subject: [PATCH 19/49] remove dead path dependency --- build.zig.zon | 3 --- 1 file changed, 3 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 4f4b9482178f3e283149d0a7e651a8d98a4893c7..5f21b86ab2ba271758c3f425709558cd77bc2fa8 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -7,9 +7,6 @@ .standalone_test_cases = .{ .path = "test/standalone", }, - .link_test_cases = .{ - .path = "test/link", - }, }, .paths = .{""}, .fingerprint = 0xc1ce108124179e16, -- 2.54.0 From bda7f21f16e5ed017dc853e20fc79c3eb6f39647 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 01:22:00 -0700 Subject: [PATCH 20/49] Maker: provide --dep import aliases on CLI --- lib/compiler/Maker.zig | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index fe27a158800a7988690c7611a27c0f00a52e01fa..35510c6f0be03815a3704f49b5de5bf030ccf0ef 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -116,9 +116,13 @@ pub const CliModule = struct { fn lower(cm: *const CliModule, arena: Allocator, gpa: Allocator, argv: *std.ArrayList([]const u8)) !void { try argv.ensureUnusedCapacity(gpa, 2 * cm.deps.count() + 1); - for (cm.deps.values()) |dep| { + for (cm.deps.keys(), cm.deps.values()) |name, dep| { argv.appendAssumeCapacity("--dep"); - argv.appendAssumeCapacity(dep.name); + if (mem.eql(u8, name, dep.name)) { + argv.appendAssumeCapacity(dep.name); + } else { + argv.appendAssumeCapacity(try allocPrint(arena, "{s}={s}", .{ name, dep.name })); + } } argv.appendAssumeCapacity(try allocPrint(arena, "-M{s}={s}", .{ cm.name, cm.root_path })); } @@ -922,12 +926,18 @@ pub fn main(init: process.Init.Minimal) !void { build_configurer_argv.shrinkRetainingCapacity(argv_deps_index); for (deps_mod.deps.values()) |dep| { try build_configurer_argv.ensureUnusedCapacity(gpa, 2 * dep.deps.count() + 1); - for (dep.deps.values()) |sub| { + for (dep.deps.keys(), dep.deps.values()) |name, sub| { build_configurer_argv.appendAssumeCapacity("--dep"); - build_configurer_argv.appendAssumeCapacity(sub.name); + if (mem.eql(u8, name, sub.name)) { + build_configurer_argv.appendAssumeCapacity(sub.name); + } else { + build_configurer_argv.appendAssumeCapacity(try allocPrint(arena, "{s}={s}", .{ + name, sub.name, + })); + } } - build_configurer_argv.appendAssumeCapacity(try allocPrint(arena, "-M{s}={s}", .{ - dep.name, dep.root_path, + build_configurer_argv.appendAssumeCapacity(try allocPrint(arena, "-M{s}={s}/{s}", .{ + dep.name, dep.root_path, std.zig.build_zig_basename, })); } try deps_mod.lower(arena, gpa, &build_configurer_argv); -- 2.54.0 From fac2cf8419647fdc3f2e399b24f7a309ce1e50de Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 01:26:43 -0700 Subject: [PATCH 21/49] compiler: pass only subcommand args to jitCmd otherwise it gets "build" too many times --- src/main.zig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main.zig b/src/main.zig index acfb05675a19230b70f1b2ddaf29701a1856b59f..c8ef6f25d00c32b523e7c430801936bdb65d57ed 100644 --- a/src/main.zig +++ b/src/main.zig @@ -353,7 +353,7 @@ fn mainArgs( return process.exit(try llvmArMain(arena, args)); }, .build, .fetch, .init => { - return jitCmd(gpa, arena, io, args, environ_map, .{ + return jitCmd(gpa, arena, io, cmd_args, environ_map, .{ .cmd_name = "maker", .root_src_path = "Maker.zig", .prepend_cmd = cmd, @@ -5070,8 +5070,10 @@ fn jitCmdInner( child_argv.appendSliceAssumeCapacity(args); if (EnvVar.ZIG_DEBUG_CMD.isSet(environ_map)) { - const cmd = try std.mem.join(arena, " ", child_argv.items); - std.debug.print("{s}\n", .{cmd}); + const cmd: std.zig.SubprocessCommand = .{ + .argv = child_argv.items, + }; + std.log.info("{f}", .{cmd}); } if (process.can_replace and options.capture == null) { -- 2.54.0 From a802ec7b3e77cf850fad57b4b56cba55426b357e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 11:14:47 -0700 Subject: [PATCH 22/49] compiler: add jit_command to core dev environment this code is lightweight and is now a dependency of building from source. it still is disabled for bootstrap however. --- src/dev.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dev.zig b/src/dev.zig index 8d3723ec101b8984b9d33381754ec3388374d3f6..47acafc9ce5ba1e279b32624b0190b891792d8c1 100644 --- a/src/dev.zig +++ b/src/dev.zig @@ -107,11 +107,11 @@ pub const Env = enum { .wasm_linker, .spirv_linker, .plan9_linker, - => true, - .cc_command, - .translate_c_command, - .fmt_command, .jit_command, + => true, + .cc_command, + .translate_c_command, + .fmt_command, .init_command, .targets_command, .version_command, -- 2.54.0 From d4b70505551ecf568f8af7375cb0e6e5aea97cfb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 01:28:12 -0700 Subject: [PATCH 23/49] cmake: remove --zig-lib-dir arg from zig build This demonstrates one of the downsides of the approach in this branch: since `zig build` CLI is intentionally unaware of the command line parsing that will be done by the child process, it cannot obtain zig lib dir override from the command line even as it needs that information in order to build the subcommand. Consequently, zig lib dir has to be passed via env var. CMake does not provide a way to do this. So instead we rely on Zig's automatic lookup functionality. This could be mitigated by adding CLI arg parsing that happens *before* the subcommand. I find this kind of special case to be awkward and unpleasant however. I think it's better to simply remove the ability to pass `--zig-lib-dir` to `zig build`. --- CMakeLists.txt | 2 -- ci/aarch64-freebsd-debug.sh | 4 +++- ci/aarch64-freebsd-release.sh | 4 +++- ci/aarch64-linux-debug.sh | 4 +++- ci/aarch64-linux-release.sh | 4 +++- ci/aarch64-macos-debug.sh | 4 +++- ci/aarch64-macos-release.sh | 4 +++- ci/aarch64-netbsd-debug.sh | 4 +++- ci/aarch64-netbsd-release.sh | 4 +++- ci/aarch64-windows.ps1 | 5 +++-- ci/loongarch64-linux-debug.sh | 4 +++- ci/loongarch64-linux-release.sh | 4 +++- ci/powerpc64le-linux-debug.sh | 4 +++- ci/powerpc64le-linux-release.sh | 4 +++- ci/riscv64-linux-debug.sh | 4 +++- ci/riscv64-linux-release.sh | 4 +++- ci/s390x-linux-debug.sh | 4 +++- ci/s390x-linux-release.sh | 4 +++- ci/x86_64-freebsd-debug.sh | 4 +++- ci/x86_64-freebsd-release.sh | 4 +++- ci/x86_64-linux-debug-llvm.sh | 4 +++- ci/x86_64-linux-debug.sh | 4 +++- ci/x86_64-linux-release.sh | 7 ++++--- ci/x86_64-netbsd-debug.sh | 4 +++- ci/x86_64-netbsd-release.sh | 4 +++- ci/x86_64-openbsd-debug.sh | 4 +++- ci/x86_64-openbsd-release.sh | 4 +++- ci/x86_64-windows-debug.ps1 | 7 +++---- ci/x86_64-windows-release.ps1 | 7 +++---- 29 files changed, 85 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 893baf2951d7136d7b291e4fce5c1ac827167022..e9c24112daefb6350d78de46f765828eab78a7a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -729,8 +729,6 @@ endif() set(ZIG_BUILD_ARGS - --zig-lib-dir "${PROJECT_SOURCE_DIR}/lib" - "-Dversion-string=${RESOLVED_ZIG_VERSION}" "-Dtarget=${ZIG_TARGET_TRIPLE}" "-Dcpu=${ZIG_TARGET_MCPU}" diff --git a/ci/aarch64-freebsd-debug.sh b/ci/aarch64-freebsd-debug.sh index f84a683771b456066ce5c7997b12bae71aac27bb..f1e53270618c5954a0554934a4dd90e81e8d5e3f 100755 --- a/ci/aarch64-freebsd-debug.sh +++ b/ci/aarch64-freebsd-debug.sh @@ -40,12 +40,14 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 4m stage3-debug/bin/zig build \ diff --git a/ci/aarch64-freebsd-release.sh b/ci/aarch64-freebsd-release.sh index e008d4ee81fb11bdeac967f93965d7bffb754c5d..0f5a949b7a89c0256f4c9d4d8e85607756b88c00 100755 --- a/ci/aarch64-freebsd-release.sh +++ b/ci/aarch64-freebsd-release.sh @@ -40,12 +40,14 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 4m # Ensure that stage3 and stage4 are byte-for-byte identical. diff --git a/ci/aarch64-linux-debug.sh b/ci/aarch64-linux-debug.sh index 5ee0a33c1ccce4272f0a26a833c4cee56e5b7db5..c8497814fdc6688e6b1d9f6d307afbe9bbd0ac88 100755 --- a/ci/aarch64-linux-debug.sh +++ b/ci/aarch64-linux-debug.sh @@ -42,6 +42,9 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + # No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts. stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ @@ -49,7 +52,6 @@ stage3-debug/bin/zig build test docs \ -Dskip-non-native \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ -Denable-superhtml \ --test-timeout 3m diff --git a/ci/aarch64-linux-release.sh b/ci/aarch64-linux-release.sh index ae5203195d5a17d6fdd4b9970b3802c3d4115ca1..d6bf710555d13145ea7978d152561e0cc2f87d9b 100755 --- a/ci/aarch64-linux-release.sh +++ b/ci/aarch64-linux-release.sh @@ -42,6 +42,9 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + # No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts. stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ @@ -49,7 +52,6 @@ stage3-release/bin/zig build test docs \ -Dskip-non-native \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ -Denable-superhtml \ --test-timeout 3m diff --git a/ci/aarch64-macos-debug.sh b/ci/aarch64-macos-debug.sh index dd959ef0c97d306d3f79a40d61d0eba87ae5c268..226b3109eafa9e8de169999b52fcd748ddb7c088 100755 --- a/ci/aarch64-macos-debug.sh +++ b/ci/aarch64-macos-debug.sh @@ -43,9 +43,11 @@ cmake .. \ ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ - --zig-lib-dir "$PWD/../lib" \ -Denable-macos-sdk \ -Dstatic-llvm \ -Dskip-spirv \ diff --git a/ci/aarch64-macos-release.sh b/ci/aarch64-macos-release.sh index 5f2012d268fd097215113c760320092aba529ad0..b1b431f257fd867bf2bacea31dc63dff3d4a3acb 100755 --- a/ci/aarch64-macos-release.sh +++ b/ci/aarch64-macos-release.sh @@ -43,8 +43,10 @@ cmake .. \ ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + stage3-release/bin/zig build test docs \ - --zig-lib-dir "$PWD/../lib" \ -Denable-macos-sdk \ -Dstatic-llvm \ -Dskip-spirv \ diff --git a/ci/aarch64-netbsd-debug.sh b/ci/aarch64-netbsd-debug.sh index 1fedad9875d77822bd5473b3603248ecaece2314..a7d7d3e97920a95ff4006792c1bea005bc648583 100755 --- a/ci/aarch64-netbsd-debug.sh +++ b/ci/aarch64-netbsd-debug.sh @@ -40,12 +40,14 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 4m stage3-debug/bin/zig build \ diff --git a/ci/aarch64-netbsd-release.sh b/ci/aarch64-netbsd-release.sh index db59d529e928f0059f664bd3557cde654d803f92..e0a4c9aaa03f87770b013be6aaf92ffddc6d2bbc 100755 --- a/ci/aarch64-netbsd-release.sh +++ b/ci/aarch64-netbsd-release.sh @@ -40,12 +40,14 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 4m # Ensure that stage3 and stage4 are byte-for-byte identical. diff --git a/ci/aarch64-windows.ps1 b/ci/aarch64-windows.ps1 index c711127a61fd0a8c48617a67199757fb2c3a7ae1..3268dfd1e4eec456d247780beab3ae2542eb6897 100644 --- a/ci/aarch64-windows.ps1 +++ b/ci/aarch64-windows.ps1 @@ -4,7 +4,6 @@ $MCPU = "baseline" $ZIG_LLVM_CLANG_LLD_URL = "https://ziglang.org/deps/$ZIG_LLVM_CLANG_LLD_NAME.zip" $PREFIX_PATH = "$(Get-Location)\..\$ZIG_LLVM_CLANG_LLD_NAME" $ZIG = "$PREFIX_PATH\bin\zig.exe" -$ZIG_LIB_DIR = "$(Get-Location)\lib" $ZSF_MAX_RSS = if ($Env:ZSF_MAX_RSS) { $Env:ZSF_MAX_RSS } else { 0 } if (!(Test-Path "..\$ZIG_LLVM_CLANG_LLD_NAME.zip")) { @@ -53,10 +52,12 @@ CheckLastExitCode ninja install CheckLastExitCode +# Must be done after zig cc is finished. +$Env:ZIG_LIB_DIR="$(Get-Location)\..\lib" + Write-Output "Main test suite..." & "stage3-release\bin\zig.exe" build test docs ` --maxrss $ZSF_MAX_RSS ` - --zig-lib-dir "$ZIG_LIB_DIR" ` --search-prefix "$PREFIX_PATH" ` -Dstatic-llvm ` -Dskip-non-native ` diff --git a/ci/loongarch64-linux-debug.sh b/ci/loongarch64-linux-debug.sh index f980744f8d0d3f7f790e88147635691cbee720f7..b3460be8fe4585bda04f9702c6d666dcf47949c5 100755 --- a/ci/loongarch64-linux-debug.sh +++ b/ci/loongarch64-linux-debug.sh @@ -40,6 +40,9 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + # No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts. stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ @@ -47,7 +50,6 @@ stage3-debug/bin/zig build test docs \ -Dskip-non-native \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 4m stage3-debug/bin/zig build \ diff --git a/ci/loongarch64-linux-release.sh b/ci/loongarch64-linux-release.sh index 0343259929fb06d39cfeda396c326f9b0cabbba9..4c1451a09003a53cde93328d34aad08c7de272f0 100755 --- a/ci/loongarch64-linux-release.sh +++ b/ci/loongarch64-linux-release.sh @@ -40,6 +40,9 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + # No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts. stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ @@ -47,7 +50,6 @@ stage3-release/bin/zig build test docs \ -Dskip-non-native \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 4m # Ensure that stage3 and stage4 are byte-for-byte identical. diff --git a/ci/powerpc64le-linux-debug.sh b/ci/powerpc64le-linux-debug.sh index cbdfd9f48fa7c228e499c23b93ca71879c5f8474..442a7a03c3637b95249b4273469348a418b784ce 100755 --- a/ci/powerpc64le-linux-debug.sh +++ b/ci/powerpc64le-linux-debug.sh @@ -40,6 +40,9 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + # No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts. stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ @@ -48,7 +51,6 @@ stage3-debug/bin/zig build test docs \ -Dtarget=native-native-musl \ -Dcpu=native+longcall \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 4m stage3-debug/bin/zig build \ diff --git a/ci/powerpc64le-linux-release.sh b/ci/powerpc64le-linux-release.sh index 25150408a4ef2161eb171371796b229a5f768ed6..c03c1504febc9e8039a74781c091fc999dd5d9e8 100755 --- a/ci/powerpc64le-linux-release.sh +++ b/ci/powerpc64le-linux-release.sh @@ -40,6 +40,9 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + # No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts. stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ @@ -48,7 +51,6 @@ stage3-release/bin/zig build test docs \ -Dtarget=native-native-musl \ -Dcpu=native+longcall \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 4m # Ensure that stage3 and stage4 are byte-for-byte identical. diff --git a/ci/riscv64-linux-debug.sh b/ci/riscv64-linux-debug.sh index 631573cfec76fc58be737b087a60a52ed0b04887..f01fc2bb09d763e3dbd06abbc055148d7d1b5ba0 100755 --- a/ci/riscv64-linux-debug.sh +++ b/ci/riscv64-linux-debug.sh @@ -42,6 +42,9 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + # No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts. stage3-debug/bin/zig build test-modules test-c-abi \ --maxrss ${ZSF_MAX_RSS:-0} \ @@ -50,5 +53,4 @@ stage3-debug/bin/zig build test-modules test-c-abi \ -Dskip-single-threaded \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 4m diff --git a/ci/riscv64-linux-release.sh b/ci/riscv64-linux-release.sh index 1f51b7d8c2931b4344bc05f9d7e76021ec35e590..34719e9d0d9595ff05f9d99f600b74347ddcb208 100755 --- a/ci/riscv64-linux-release.sh +++ b/ci/riscv64-linux-release.sh @@ -42,6 +42,9 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + # No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts. stage3-release/bin/zig build test-modules test-c-abi \ --maxrss ${ZSF_MAX_RSS:-0} \ @@ -50,5 +53,4 @@ stage3-release/bin/zig build test-modules test-c-abi \ -Dskip-single-threaded \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 4m diff --git a/ci/s390x-linux-debug.sh b/ci/s390x-linux-debug.sh index f4d601ce4bc74fe5244b3bd596f4e75684eaefe1..3d97f9c1650189ae1f105553257dd692e94f818d 100755 --- a/ci/s390x-linux-debug.sh +++ b/ci/s390x-linux-debug.sh @@ -40,6 +40,9 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + # No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts. stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ @@ -47,7 +50,6 @@ stage3-debug/bin/zig build test docs \ -Dskip-non-native \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 4m stage3-debug/bin/zig build \ diff --git a/ci/s390x-linux-release.sh b/ci/s390x-linux-release.sh index db70925e67b6afea80b483821e0a9a47d011f4a0..a8c5a9fcb341bb616729c5ab792acff29ca3db02 100755 --- a/ci/s390x-linux-release.sh +++ b/ci/s390x-linux-release.sh @@ -40,6 +40,9 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + # No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts. stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ @@ -47,7 +50,6 @@ stage3-release/bin/zig build test docs \ -Dskip-non-native \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 4m # Ensure that stage3 and stage4 are byte-for-byte identical. diff --git a/ci/x86_64-freebsd-debug.sh b/ci/x86_64-freebsd-debug.sh index ec8d0db1ad77db75111ad334edca9094ffc03419..c67c7bbe4d50bdd2448a83e0479ff61b65cc5a62 100755 --- a/ci/x86_64-freebsd-debug.sh +++ b/ci/x86_64-freebsd-debug.sh @@ -40,6 +40,9 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ @@ -51,7 +54,6 @@ stage3-debug/bin/zig build test docs \ -Dskip-windows \ -Dskip-darwin \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 2m stage3-debug/bin/zig build \ diff --git a/ci/x86_64-freebsd-release.sh b/ci/x86_64-freebsd-release.sh index f3d50ee920e1ff5ba8cf9a128b1c4a8f32e5df0d..83c88ef5aae163deee3cebd40c1b2b1f01047930 100755 --- a/ci/x86_64-freebsd-release.sh +++ b/ci/x86_64-freebsd-release.sh @@ -40,6 +40,9 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ @@ -51,7 +54,6 @@ stage3-release/bin/zig build test docs \ -Dskip-windows \ -Dskip-darwin \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 2m # Ensure that the fuzzer at least compiles. diff --git a/ci/x86_64-linux-debug-llvm.sh b/ci/x86_64-linux-debug-llvm.sh index 77579c827ba9414d773afddb1c8da47a5230e518..408607dbc632d110e82cca0dadcd0980b3bebe85 100755 --- a/ci/x86_64-linux-debug-llvm.sh +++ b/ci/x86_64-linux-debug-llvm.sh @@ -43,6 +43,9 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + # simultaneously test building self-hosted without LLVM and with 32-bit arm stage3-debug/bin/zig build \ -Dtarget=arm-linux-musleabihf \ @@ -63,7 +66,6 @@ stage3-debug/bin/zig build test docs \ -Dskip-darwin \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ -Denable-superhtml \ --test-timeout 12m diff --git a/ci/x86_64-linux-debug.sh b/ci/x86_64-linux-debug.sh index 690b9c3314700f6a4b48ac7bc29adfe2e90bace4..fe4450ea8e67316d5d75def9c60fb79bf6edb3c6 100755 --- a/ci/x86_64-linux-debug.sh +++ b/ci/x86_64-linux-debug.sh @@ -42,6 +42,9 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + # simultaneously test building self-hosted without LLVM and with 32-bit arm stage3-debug/bin/zig build \ -Dtarget=arm-linux-musleabihf \ @@ -63,7 +66,6 @@ stage3-debug/bin/zig build test docs \ -Dskip-llvm \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ -Denable-superhtml \ --test-timeout 10m diff --git a/ci/x86_64-linux-release.sh b/ci/x86_64-linux-release.sh index 815e85bf60f497585ca2d34704c6c8bd4d0d0e89..0fbccca8d3291869b1d68e461417053524b2b109 100755 --- a/ci/x86_64-linux-release.sh +++ b/ci/x86_64-linux-release.sh @@ -48,6 +48,9 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + # Covers several things: # 1. building the compiler without LLVM # 2. 32-bit @@ -66,7 +69,6 @@ stage3-release/bin/zig build test docs \ -Dstatic-llvm \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ -Denable-superhtml \ --test-timeout 12m @@ -120,6 +122,5 @@ stage3/bin/zig build -p stage4 \ -Dstatic-llvm \ -Dtarget=native-native-musl \ -Dno-lib \ - --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" + --search-prefix "$PREFIX" stage4/bin/zig test ../test/behavior.zig diff --git a/ci/x86_64-netbsd-debug.sh b/ci/x86_64-netbsd-debug.sh index 3755131c6d1a815ecf1e1392f1e14be759af0ecb..ef16be259f9a5c692d63e094f974c81ee4e0e62d 100755 --- a/ci/x86_64-netbsd-debug.sh +++ b/ci/x86_64-netbsd-debug.sh @@ -40,12 +40,14 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 2m stage3-debug/bin/zig build \ diff --git a/ci/x86_64-netbsd-release.sh b/ci/x86_64-netbsd-release.sh index 2a3dde40bd6f28f4c2ccec953307a29178d79d9d..f9fc7d6aea7fa0ed44223544052dd084a06996fd 100755 --- a/ci/x86_64-netbsd-release.sh +++ b/ci/x86_64-netbsd-release.sh @@ -40,12 +40,14 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 2m # Ensure that the fuzzer at least compiles. diff --git a/ci/x86_64-openbsd-debug.sh b/ci/x86_64-openbsd-debug.sh index e85befacfbf653af9d650a898effee6c860cda05..3aac9c636bc642dca609d562c6f721ce6cce8665 100755 --- a/ci/x86_64-openbsd-debug.sh +++ b/ci/x86_64-openbsd-debug.sh @@ -40,12 +40,14 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 2m stage3-debug/bin/zig build \ diff --git a/ci/x86_64-openbsd-release.sh b/ci/x86_64-openbsd-release.sh index ea9c44df11318118c0dbd7cc1a01838adc2a0116..76dd9a8b9c0a41efe1a89a21a7125f56f6066d44 100755 --- a/ci/x86_64-openbsd-release.sh +++ b/ci/x86_64-openbsd-release.sh @@ -40,12 +40,14 @@ unset CXX ninja install +# Must be done after zig cc is finished. +export ZIG_LIB_DIR="$PWD/../lib" + stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" \ --test-timeout 2m # Ensure that the fuzzer at least compiles. diff --git a/ci/x86_64-windows-debug.ps1 b/ci/x86_64-windows-debug.ps1 index a00f6812e0382c5257cf00ae22cd7dd0de3d277a..fa416bf3ff1207dc26c6ebf497608940d8fc52e7 100644 --- a/ci/x86_64-windows-debug.ps1 +++ b/ci/x86_64-windows-debug.ps1 @@ -2,7 +2,6 @@ $TARGET = "x86_64-windows-gnu" $MCPU = "baseline" $PREFIX_PATH = "$($Env:USERPROFILE)\deps\zig+llvm+lld+clang-$TARGET-0.17.0-dev.203+073889523" $ZIG = "$PREFIX_PATH\bin\zig.exe" -$ZIG_LIB_DIR = "$(Get-Location)\lib" $ZSF_MAX_RSS = if ($Env:ZSF_MAX_RSS) { $Env:ZSF_MAX_RSS } else { 0 } function CheckLastExitCode { @@ -42,10 +41,12 @@ CheckLastExitCode ninja install CheckLastExitCode +# Must be done after zig cc is finished. +$Env:ZIG_LIB_DIR="$(Get-Location)\..\lib" + Write-Output "Main test suite..." stage3-debug\bin\zig build test docs ` --maxrss $ZSF_MAX_RSS ` - --zig-lib-dir "$ZIG_LIB_DIR" ` --search-prefix "$PREFIX_PATH" ` -Dstatic-llvm ` -Dskip-non-native ` @@ -56,7 +57,6 @@ CheckLastExitCode Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..." stage3-debug\bin\zig build-obj ` - --zig-lib-dir "$ZIG_LIB_DIR" ` -ofmt=c ` -OReleaseSmall ` --name compiler_rt ` @@ -67,7 +67,6 @@ stage3-debug\bin\zig build-obj ` CheckLastExitCode stage3-debug\bin\zig test ` - --zig-lib-dir "$ZIG_LIB_DIR" ` -ofmt=c ` -femit-bin="behavior-x86_64-windows-msvc.c" ` --test-no-exec ` diff --git a/ci/x86_64-windows-release.ps1 b/ci/x86_64-windows-release.ps1 index 14d06818983be638497bc190e825fcb7734c8c92..8364836291d8d57097f7a76cba93b651c88c1e94 100644 --- a/ci/x86_64-windows-release.ps1 +++ b/ci/x86_64-windows-release.ps1 @@ -2,7 +2,6 @@ $TARGET = "x86_64-windows-gnu" $MCPU = "baseline" $PREFIX_PATH = "$($Env:USERPROFILE)\deps\zig+llvm+lld+clang-$TARGET-0.17.0-dev.203+073889523" $ZIG = "$PREFIX_PATH\bin\zig.exe" -$ZIG_LIB_DIR = "$(Get-Location)\lib" $ZSF_MAX_RSS = if ($Env:ZSF_MAX_RSS) { $Env:ZSF_MAX_RSS } else { 0 } function CheckLastExitCode { @@ -42,10 +41,12 @@ CheckLastExitCode ninja install CheckLastExitCode +# Must be done after zig cc is finished. +$Env:ZIG_LIB_DIR="$(Get-Location)\..\lib" + Write-Output "Main test suite..." stage3-release\bin\zig.exe build test docs ` --maxrss $ZSF_MAX_RSS ` - --zig-lib-dir "$ZIG_LIB_DIR" ` --search-prefix "$PREFIX_PATH" ` -Dstatic-llvm ` -Dskip-non-native ` @@ -82,7 +83,6 @@ CheckLastExitCode Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..." stage3-release\bin\zig.exe build-obj ` - --zig-lib-dir "$ZIG_LIB_DIR" ` -ofmt=c ` -OReleaseSmall ` --name compiler_rt ` @@ -93,7 +93,6 @@ stage3-release\bin\zig.exe build-obj ` CheckLastExitCode stage3-release\bin\zig.exe test ` - --zig-lib-dir "$ZIG_LIB_DIR" ` -ofmt=c ` -femit-bin="behavior-x86_64-windows-msvc.c" ` --test-no-exec ` -- 2.54.0 From 91a08af5702cba683ae2d418ff57307f159e3281 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 13:56:13 -0700 Subject: [PATCH 24/49] std.Progress: move Options closer to where it is used no functional change --- lib/std/Progress.zig | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 70c61f27e2b15d6d7f903e6d778f4bd0963b5d3c..b0f1b5f9a9011427e48c83c334c602c69c6f42c2 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -165,25 +165,6 @@ pub const TerminalMode = union(enum) { }; }; -pub const Options = struct { - /// User-provided buffer with static lifetime. - /// - /// Used to store the entire write buffer sent to the terminal. Progress output will be truncated if it - /// cannot fit into this buffer which will look bad but not cause any malfunctions. - /// - /// Must be at least 200 bytes. - draw_buffer: []u8 = &default_draw_buffer, - /// How many nanoseconds between writing updates to the terminal. - refresh_rate_ns: Io.Duration = .fromMilliseconds(80), - /// How many nanoseconds to keep the output hidden - initial_delay_ns: Io.Duration = .fromMilliseconds(200), - /// If provided, causes the progress item to have a denominator. - /// 0 means unknown. - estimated_total_items: usize = 0, - root_name: []const u8 = "", - disable_printing: bool = false, -}; - /// Represents one unit of progress. Each node can have children nodes, or /// one can use integers with `update`. pub const Node = struct { @@ -578,6 +559,25 @@ pub const ParentFileError = error{ UnrecognizedFormat, }; +pub const Options = struct { + /// User-provided buffer with static lifetime. + /// + /// Used to store the entire write buffer sent to the terminal. Progress output will be truncated if it + /// cannot fit into this buffer which will look bad but not cause any malfunctions. + /// + /// Must be at least 200 bytes. + draw_buffer: []u8 = &default_draw_buffer, + /// How many nanoseconds between writing updates to the terminal. + refresh_rate_ns: Io.Duration = .fromMilliseconds(80), + /// How many nanoseconds to keep the output hidden + initial_delay_ns: Io.Duration = .fromMilliseconds(200), + /// If provided, causes the progress item to have a denominator. + /// 0 means unknown. + estimated_total_items: usize = 0, + root_name: []const u8 = "", + disable_printing: bool = false, +}; + /// Initializes a global Progress instance. /// /// Asserts there is only one global Progress instance. -- 2.54.0 From f1f5651f24b29c89fa8ed9bdc0d1a55fa9fd79cc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 13:56:41 -0700 Subject: [PATCH 25/49] Maker: remove redundant continue statements these are relics of when this code used to live in src/main.zig --- lib/compiler/Maker.zig | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 35510c6f0be03815a3704f49b5de5bf030ccf0ef..d7c9f6c09c8f8e8439c7329977f43a83762d6031 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -256,31 +256,26 @@ pub fn main(init: process.Init.Minimal) !void { { try cached_passthru_configure.append(arena, @intCast(configure_argv.items.len)); configure_argv.appendAssumeCapacity(arg); - continue; } 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); // Intentionally "--system" only; not the path. - continue; } else if (mem.cutPrefix(u8, arg, "--color=")) |rest| { color = stringToEnum(Color, rest) orelse fatal("expected --color=[auto|on|off]; found {q}", .{arg}); try cached_passthru_configure.append(arena, @intCast(configure_argv.items.len)); configure_argv.appendAssumeCapacity(arg); - continue; } else if (mem.eql(u8, arg, "--cache-poison")) { cache_poison = .poisoned; configure_argv.appendAssumeCapacity("--cache-poison=poisoned"); - continue; } else if (mem.cutPrefix(u8, arg, "--cache-poison=")) |rest| { // Allow the configurer process to report parse failure. if (stringToEnum(std.Build.Graph.CachePoison, rest)) |poison| { cache_poison = poison; } configure_argv.appendAssumeCapacity(arg); - continue; } else if (mem.eql(u8, arg, "--verbose")) { // Intentionally is added both to make and configure but // does not go into the cache hash. @@ -291,7 +286,6 @@ pub fn main(init: process.Init.Minimal) !void { // the cache and configurer must set the poison bit when // choosing to observe it. configure_argv.addManyAsArrayAssumeCapacity(2).* = .{ arg, prefix }; - continue; } else if (mem.eql(u8, arg, "--cache-dir")) { override_local_cache_dir = nextArgOrFatal(args, &arg_i); } else if (mem.eql(u8, arg, "--pkg-dir")) { -- 2.54.0 From ef76b925c55ce5eadb614f4d640bfef5eec9518a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 13:57:06 -0700 Subject: [PATCH 26/49] Maker.Step.Compile: tweak error messages - prefer {q} over '{s}' - prefer unquoted over '{s}/foo' --- lib/compiler/Maker/Step/Compile.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compiler/Maker/Step/Compile.zig b/lib/compiler/Maker/Step/Compile.zig index 69dae4610f8a10f70d25316da2a4bfec6234d63a..33a096027032fddd94349ebc216089fbe9ea564e 100644 --- a/lib/compiler/Maker/Step/Compile.zig +++ b/lib/compiler/Maker/Step/Compile.zig @@ -777,7 +777,7 @@ fn lowerZigArgs( for (graph.search_prefixes.items) |search_prefix| { var prefix_dir = cwd.openDir(io, search_prefix, .{}) catch |err| { - return step.fail(maker, "unable to open prefix directory '{s}': {t}", .{ search_prefix, err }); + return step.fail(maker, "unable to open prefix directory {q}: {t}", .{ search_prefix, err }); }; defer prefix_dir.close(io); @@ -791,7 +791,7 @@ fn lowerZigArgs( }); } else |err| switch (err) { error.FileNotFound => {}, - else => |e| return step.fail(maker, "unable to access '{s}/lib' directory: {t}", .{ search_prefix, e }), + else => |e| return step.fail(maker, "unable to access {s}/lib directory: {t}", .{ search_prefix, e }), } if (prefix_dir.access(io, "include", .{})) |_| { @@ -800,7 +800,7 @@ fn lowerZigArgs( }); } else |err| switch (err) { error.FileNotFound => {}, - else => |e| return step.fail(maker, "unable to access '{s}/include' directory: {t}", .{ search_prefix, e }), + else => |e| return step.fail(maker, "unable to access {s}/include directory: {t}", .{ search_prefix, e }), } } -- 2.54.0 From 7b4f0ef133a5b03e0ec6ea73b1681ed3ac21a31e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 15:03:01 -0700 Subject: [PATCH 27/49] compiler: name progress node for jitCmd --- src/main.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.zig b/src/main.zig index c8ef6f25d00c32b523e7c430801936bdb65d57ed..2213920e32ac4402c401f9b1cdf7b8abe9c75c8b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4897,6 +4897,7 @@ fn jitCmd( const root_prog_node = std.Progress.start(io, .{ .disable_printing = (color == .off), + .root_name = try allocPrint(arena, "Compiling {s} (first time setup)", .{options.cmd_name}), }); defer root_prog_node.end(); -- 2.54.0 From 46bdb35420efc111a38bba19f9bd714ad41a759f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 16:45:14 -0700 Subject: [PATCH 28/49] Maker: fix CLI flag parsing when combining two processes into one, the same flags were parsed multiple times. This caused some of the logic for --verbose, --color, and --search-prefixes to be incorrectly dead. --- lib/compiler/Maker.zig | 28 ++++++++++++++-------------- lib/compiler/Maker/ScannedConfig.zig | 2 -- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index d7c9f6c09c8f8e8439c7329977f43a83762d6031..32d065ede20a52a4d2d3ba89f89c2dfe7402e91d 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -1,4 +1,5 @@ const Maker = @This(); + const builtin = @import("builtin"); const native_os = builtin.os.tag; @@ -263,10 +264,17 @@ pub fn main(init: process.Init.Minimal) !void { configure_argv.appendAssumeCapacity(arg); // Intentionally "--system" only; not the path. } else if (mem.cutPrefix(u8, arg, "--color=")) |rest| { color = stringToEnum(Color, rest) orelse - fatal("expected --color=[auto|on|off]; found {q}", .{arg}); + 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 allocPrint(arena, "--color={t}", .{color})); } else if (mem.eql(u8, arg, "--cache-poison")) { cache_poison = .poisoned; configure_argv.appendAssumeCapacity("--cache-poison=poisoned"); @@ -280,12 +288,16 @@ pub fn main(init: process.Init.Minimal) !void { // Intentionally is added both to make and configure but // 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); + // This argument is cache poisonous: it does not go into // 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")) { @@ -361,18 +373,8 @@ pub fn main(init: process.Init.Minimal) !void { .{ timeout_str, num_str, err }, ); test_timeout_ns = std.math.lossyCast(u64, unit_factor * num_parsed); - } else if (mem.eql(u8, arg, "--search-prefix")) { - try graph.search_prefixes.append(arena, nextArgOrFatal(args, &arg_i)); } else if (mem.eql(u8, arg, "--libc")) { graph.libc_file = nextArgOrFatal(args, &arg_i); - } else if (mem.eql(u8, arg, "--color")) { - const next_arg = nextArg(args, &arg_i) orelse - fatalWithHint("expected [auto|on|off] after {q}", .{arg}); - color = stringToEnum(Color, next_arg) orelse { - fatalWithHint("expected [auto|on|off] after {q}, found {q}", .{ - arg, next_arg, - }); - }; } else if (mem.eql(u8, arg, "--error-style")) { const next_arg = nextArg(args, &arg_i) orelse fatalWithHint("expected style after {q}", .{arg}); @@ -429,15 +431,13 @@ pub fn main(init: process.Init.Minimal) !void { } 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.builtin.OptimizeMode, rest) orelse + 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")) { // --glibc-runtimes was the old name of the flag; kept for compatibility for now. graph.libc_runtimes_dir = nextArgOrFatal(args, &arg_i); - } else if (mem.eql(u8, arg, "--verbose")) { - graph.verbose = true; } else if (mem.eql(u8, arg, "--verbose-air")) { graph.verbose_air = true; } else if (mem.eql(u8, arg, "--verbose-cc")) { diff --git a/lib/compiler/Maker/ScannedConfig.zig b/lib/compiler/Maker/ScannedConfig.zig index fe42bfd81459fe52420a06ecf3133bfbcb613745..8421cd008547b17ff14f1c416d64040d6c33f56e 100644 --- a/lib/compiler/Maker/ScannedConfig.zig +++ b/lib/compiler/Maker/ScannedConfig.zig @@ -342,7 +342,6 @@ pub fn printUsage(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void { \\ --build-file [file] Override path to build.zig \\ --cache-dir [path] Override path to local Zig cache directory \\ --global-cache-dir [path] Override path to global Zig cache directory - \\ --zig-lib-dir [arg] Override path to Zig lib directory \\ --seed [integer] For shuffling dependency traversal order (default: random) \\ --cache-poison[=mode] Override configuration caching behavior \\ pure (default) Avoid false positive cache hits @@ -360,7 +359,6 @@ pub fn printUsage(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void { \\ none (default) No build ID \\ --debug-log [scope] Enable debugging the compiler \\ --debug-pkg-config Fail if unknown pkg-config flags encountered - \\ --maker-opt=[mode] Change maker executable optimization mode (default: ReleaseSafe) \\ --verbose-link Enable compiler debug output for linking \\ --verbose-air Enable compiler debug output for Zig AIR \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR -- 2.54.0 From 09f61b13b359eec02d12567b3bd2c967c5448e81 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 17:25:12 -0700 Subject: [PATCH 29/49] std.zig.buildExeSubprocess: remove log, return more info --- lib/compiler/Maker.zig | 2 +- lib/compiler/Maker/WebServer.zig | 4 +++- lib/std/zig.zig | 23 ++++++++++++++++++----- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 32d065ede20a52a4d2d3ba89f89c2dfe7402e91d..71e8c7c9faa5be0ea2efed337d40f41f93030b24 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -965,7 +965,7 @@ pub fn main(init: process.Init.Minimal) !void { .cache_manifest = &config_man, .arch_os_abi = target_arch_os_abi, .progress_node = compile_prog_node, - })) |p| p else |err| switch (err) { + })) |r| r.path else |err| switch (err) { error.AlreadyReported => process.exit(1), // If the file system inputs are populated, we can // still watch for changes and try again. diff --git a/lib/compiler/Maker/WebServer.zig b/lib/compiler/Maker/WebServer.zig index dddea1600beef0e0187162584349c5c0cd1df84d..7a8c5013611c196ede9c3e6390e2fa9989c41eab 100644 --- a/lib/compiler/Maker/WebServer.zig +++ b/lib/compiler/Maker/WebServer.zig @@ -625,7 +625,7 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim const compile_prog_node = ws.root_prog_node.start("Compile WebAssembly Component", 0); defer compile_prog_node.end(); - return std.zig.buildExeSubprocess(gpa, io, .{ + const result = try std.zig.buildExeSubprocess(gpa, io, .{ .argv = argv.items, .cache_root = graph.global_cache_root, .root_name = root_name, @@ -633,6 +633,8 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim .cpu_features = cpu_features, .progress_node = compile_prog_node, }); + if (!result.cache_hit) log.info("source changes detected; rebuilt wasm component", .{}); + return result.path; } pub fn updateTimeReportCompile(ws: *WebServer, opts: struct { diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 657b05638e03147e5d54d51b3768b97004333276..c875663d67e028ecccfc6e5cac6a9647fc4d7d26 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -1644,10 +1644,20 @@ pub const BuildExeSubprocessError = error{ FailedButCacheIntact, } || Io.Cancelable || Allocator.Error; +pub const BuildExeSubprocessResult = struct { + received_fs_inputs: bool, + cache_hit: bool, + path: Cache.Path, +}; + /// Assumes `argv` has `--listen=-` in it and the child process is `zig build-exe`. /// /// Result path is allocated via gpa. -pub fn buildExeSubprocess(gpa: Allocator, io: Io, options: BuildExeSubprocessOptions) BuildExeSubprocessError!Cache.Path { +pub fn buildExeSubprocess( + gpa: Allocator, + io: Io, + options: BuildExeSubprocessOptions, +) BuildExeSubprocessError!BuildExeSubprocessResult { const cmd: SubprocessCommand = .{ .argv = options.argv }; var child = std.process.spawn(io, .{ @@ -1699,6 +1709,7 @@ pub fn buildExeSubprocess(gpa: Allocator, io: Io, options: BuildExeSubprocessOpt defer body_buffer.deinit(gpa); var received_fs_inputs = false; + var cache_hit = false; while (true) { const header = stdout.takeStruct(Header, .little) catch |err| switch (err) { @@ -1739,9 +1750,7 @@ pub fn buildExeSubprocess(gpa: Allocator, io: Io, options: BuildExeSubprocessOpt .emit_digest => { const EmitDigest = Server.Message.EmitDigest; const ebp_hdr: *align(1) const EmitDigest = @ptrCast(body); - if (!ebp_hdr.flags.cache_hit) { - log.info("source changes detected; rebuilt {s}", .{options.root_name}); - } + cache_hit = ebp_hdr.flags.cache_hit; const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len]; if (result) |r| gpa.free(r.sub_path); result = .{ @@ -1831,7 +1840,11 @@ pub fn buildExeSubprocess(gpa: Allocator, io: Io, options: BuildExeSubprocessOpt .output_mode = .Exe, }); defer gpa.free(bin_name); - return base_path.join(gpa, bin_name); + return .{ + .received_fs_inputs = received_fs_inputs, + .cache_hit = cache_hit, + .path = try base_path.join(gpa, bin_name), + }; } fn readStreamAlloc(gpa: Allocator, io: Io, file: Io.File, limit: Io.Limit) ![]u8 { -- 2.54.0 From 519a47af31b0eaf41fc3649474e16f460b454795 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 17:27:59 -0700 Subject: [PATCH 30/49] Maker: fix memory leak of deps hash tables This is still basically a leak since it uses global arena but it can be adjusted later to use an arena local to the while loop iteration as part of a larger global audit. --- lib/compiler/Maker.zig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 71e8c7c9faa5be0ea2efed337d40f41f93030b24..729e9aef412dfdeb351da62513ccc18439052874 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -716,14 +716,12 @@ pub fn main(init: process.Init.Minimal) !void { .name = "@build", .root_path = try root_build_src_path.toString(arena), }; - defer build_mod.deps.deinit(gpa); const deps_mod = try arena.create(CliModule); deps_mod.* = .{ .name = "@dependencies", .root_path = undefined, }; - defer deps_mod.deps.deinit(gpa); // This loop is re-evaluated when the build script exits with an indication that it // could not continue due to missing lazy dependencies. @@ -876,7 +874,7 @@ pub fn main(init: process.Init.Minimal) !void { // Add a CliModule for each package's build.zig. const hashes = job_queue.table.keys(); const fetches = job_queue.table.values(); - try deps_mod.deps.ensureUnusedCapacity(gpa, @intCast(hashes.len)); + try deps_mod.deps.ensureUnusedCapacity(arena, @intCast(hashes.len)); for (hashes, fetches) |*hash, f| { if (f == &fetch) { // The first one is a dummy package for the current project. @@ -902,7 +900,7 @@ pub fn main(init: process.Init.Minimal) !void { if (!f.have_manifest) continue; const man = &f.manifest; const dep_names = man.dependencies.keys(); - try mod.deps.ensureUnusedCapacity(gpa, @intCast(dep_names.len)); + try mod.deps.ensureUnusedCapacity(arena, @intCast(dep_names.len)); for (dep_names, man.dependencies.values()) |name, dep| { const dep_digest = Package.Fetch.depDigest( f.package_root, -- 2.54.0 From b68192254687e279bef4196cdf0575ed748964f7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 17:49:24 -0700 Subject: [PATCH 31/49] Maker.Fetch: clarify that Cache import is not used --- lib/compiler/Maker/Fetch.zig | 47 ++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/lib/compiler/Maker/Fetch.zig b/lib/compiler/Maker/Fetch.zig index 5bd136483ec64ca8ebbe08fe4f7630499d8e4b26..d730f724d29d44c3bc733686be80df8d73a90b70 100644 --- a/lib/compiler/Maker/Fetch.zig +++ b/lib/compiler/Maker/Fetch.zig @@ -44,7 +44,8 @@ const log = std.log.scoped(.fetch); const assert = std.debug.assert; const ascii = std.ascii; const Allocator = std.mem.Allocator; -const Cache = std.Build.Cache; +const Path = std.Build.Cache.Path; +const Directory = std.Build.Cache.Directory; const git = @import("Fetch/git.zig"); const Package = @import("Package.zig"); const Manifest = Package.Manifest; @@ -58,8 +59,8 @@ name_tok: std.zig.Ast.TokenIndex, lazy_status: LazyStatus, /// Same as `parent_packge_root` except it is unchanged when recursing into /// relative file paths (as opposed to URL). -remote_package_root: Cache.Path, -parent_package_root: Cache.Path, +remote_package_root: Path, +parent_package_root: Path, parent_manifest_ast: ?*const std.zig.Ast, prog_node: std.Progress.Node, job_queue: *JobQueue, @@ -77,7 +78,7 @@ use_latest_commit: bool, // Below this are fields populated by `run`. /// Relative to the build root of the root package. -package_root: Cache.Path, +package_root: Path, error_bundle: ErrorBundle.Wip, manifest: Manifest, manifest_ast: std.zig.Ast, @@ -111,9 +112,9 @@ pub const LazyStatus = enum { }; pub const LocalStorage = struct { - cache_root: Cache.Path, + cache_root: Path, /// Path to "zig-pkg" inside the package in which the user ran `zig build`. - pkg_root: Cache.Path, + pkg_root: Path, }; /// Contains shared state among all `Fetch` tasks. @@ -133,7 +134,7 @@ pub const JobQueue = struct { http_client: *std.http.Client, /// This tracks `Fetch` tasks as well as recompression tasks. group: Io.Group = .init, - global_cache: Cache.Directory, + global_cache: Directory, /// If `null`, indicates fetch globally only. local_storage: ?*const LocalStorage, /// If true then, no fetching occurs, and: @@ -170,7 +171,7 @@ pub const JobQueue = struct { pub const ForkSet = std.array_hash_map.Custom(Fork, void, Fork.Context, false); pub const Fork = struct { - path: Cache.Path, + path: Path, manifest_ast: std.zig.Ast, manifest: Package.Manifest, uses: usize, @@ -352,14 +353,14 @@ pub const JobQueue = struct { ); } - fn recompress(jq: *JobQueue, package_hash: Package.Hash, package_root: Cache.Path) Io.Cancelable!void { + fn recompress(jq: *JobQueue, package_hash: Package.Hash, package_root: Path) Io.Cancelable!void { const pkg_hash_slice = package_hash.toSlice(); const prog_node = jq.prog_node.startFmt(0, "recompress {s}", .{pkg_hash_slice}); defer prog_node.end(); var dest_sub_path_buf: ["p/".len + Package.Hash.max_len + ".tar.gz".len]u8 = undefined; - const dest_path: Cache.Path = .{ + const dest_path: Path = .{ .root_dir = jq.global_cache, .sub_path = std.fmt.bufPrint(&dest_sub_path_buf, "p/{s}.tar.gz", .{pkg_hash_slice}) catch unreachable, }; @@ -381,9 +382,9 @@ pub const JobQueue = struct { fn recompressFallible( jq: *JobQueue, arena: Allocator, - dest_path: Cache.Path, + dest_path: Path, pkg_hash_slice: []const u8, - package_root: Cache.Path, + package_root: Path, prog_node: std.Progress.Node, ) !void { const gpa = jq.http_client.allocator; @@ -493,7 +494,7 @@ fn stringCmp(_: void, lhs: ScannedFile, rhs: ScannedFile) bool { pub const Location = union(enum) { remote: Remote, /// A directory found inside the parent package. - relative_path: Cache.Path, + relative_path: Path, /// Recursive Fetch tasks will never use this Location, but it may be /// passed in by the CLI. Indicates the file contents here should be copied /// into the global package cache. It may be a file relative to the cwd or @@ -641,7 +642,7 @@ pub fn run(f: *Fetch) RunError!void { // Check global cache before remote fetch. const cached_tarball_sub_path = try std.fmt.allocPrint(arena, "p/{s}.tar.gz", .{expected_hash.toSlice()}); - const cached_tarball_path: Cache.Path = .{ + const cached_tarball_path: Path = .{ .root_dir = job_queue.global_cache, .sub_path = cached_tarball_sub_path, }; @@ -716,7 +717,7 @@ fn runResource( }; const tmp_dir_sub_path = ".tmp-" ++ std.fmt.hex(rand_int); const tmp_tmp_dir_sub_path = "tmp/" ++ tmp_dir_sub_path; - const tmp_directory_path: Cache.Path = if (job_queue.local_storage) |ls| + const tmp_directory_path: Path = if (job_queue.local_storage) |ls| try ls.pkg_root.join(arena, tmp_dir_sub_path) else .{ @@ -725,7 +726,7 @@ fn runResource( }; const package_sub_path = blk: { - var tmp_directory: Cache.Directory = .{ + var tmp_directory: Directory = .{ .path = tmp_directory_path.sub_path, .handle = handle: { const dir = tmp_directory_path.root_dir.handle.createDirPathOpen(io, tmp_directory_path.sub_path, .{ @@ -746,7 +747,7 @@ fn runResource( // Fetch and unpack a resource into a temporary directory. var unpack_result = try unpackResource(f, resource, uri_path, tmp_directory); - const pkg_path: Cache.Path = .{ .root_dir = tmp_directory, .sub_path = unpack_result.root_dir }; + const pkg_path: Path = .{ .root_dir = tmp_directory, .sub_path = unpack_result.root_dir }; // Load, parse, and validate the unpacked build.zig.zon file. It is allowed // for the file to be missing, in which case this fetched package is @@ -874,7 +875,7 @@ fn checkBuildFileExistence(f: *Fetch) RunError!void { } /// This function populates `f.manifest` or leaves it `null`. -fn loadManifest(f: *Fetch, pkg_root: Cache.Path) RunError!void { +fn loadManifest(f: *Fetch, pkg_root: Path) RunError!void { const io = f.job_queue.io; const eb = &f.error_bundle; const arena = f.arena.allocator(); @@ -1038,7 +1039,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void { } } -pub fn relativePathDigest(pkg_root: Cache.Path, cache_root: Cache.Directory) Package.Hash { +pub fn relativePathDigest(pkg_root: Path, cache_root: Directory) Package.Hash { return .initPath(pkg_root.sub_path, pkg_root.root_dir.eql(cache_root)); } @@ -1331,7 +1332,7 @@ fn unpackResource( f: *Fetch, resource: *Resource, uri_path: []const u8, - tmp_directory: Cache.Directory, + tmp_directory: Directory, ) RunError!UnpackResult { const eb = &f.error_bundle; const file_type = switch (resource.*) { @@ -1667,7 +1668,7 @@ fn recursiveDirectoryCopy(f: *Fetch, dir: Io.Dir, tmp_dir: Io.Dir) anyerror!void } } -pub fn renameTmpIntoCache(io: Io, tmp_path: Cache.Path, dest_path: Cache.Path) !void { +pub fn renameTmpIntoCache(io: Io, tmp_path: Path, dest_path: Path) !void { var handled_missing_dir = false; while (true) { Io.Dir.rename( @@ -1711,7 +1712,7 @@ const ComputedHash = struct { /// the hash are not present on the file system. Empty directories are *not /// hashed* and must not be present on the file system when calling this /// function. -fn computeHash(f: *Fetch, pkg_path: Cache.Path, filter: Filter) RunError!ComputedHash { +fn computeHash(f: *Fetch, pkg_path: Path, filter: Filter) RunError!ComputedHash { const io = f.job_queue.io; // All the path name strings need to be in memory for sorting. const arena = f.arena.allocator(); @@ -2035,7 +2036,7 @@ const Filter = struct { } }; -pub fn depDigest(pkg_root: Cache.Path, cache_root: Cache.Directory, dep: Manifest.Dependency) ?Package.Hash { +pub fn depDigest(pkg_root: Path, cache_root: Directory, dep: Manifest.Dependency) ?Package.Hash { if (dep.hash) |h| return .fromSlice(h); switch (dep.location) { -- 2.54.0 From a93d855a0f0d61e57142cee0e1c4b5de8c083cc2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 20:01:56 -0700 Subject: [PATCH 32/49] Maker: restructure such that configuration can be repeated --- lib/compiler/Maker.zig | 1465 ++++++++++++++++-------------- lib/compiler/Maker/Fuzz.zig | 8 +- lib/compiler/Maker/Step.zig | 2 +- lib/compiler/Maker/Step/Run.zig | 2 +- lib/compiler/Maker/WebServer.zig | 302 +++--- 5 files changed, 937 insertions(+), 842 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 729e9aef412dfdeb351da62513ccc18439052874..436ff43895cd049af0cf528fb789575cc83a86d2 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -52,7 +52,7 @@ max_rss_mutex: Io.Mutex, skip_oom_steps: bool, unit_test_timeout_ns: ?u64, watch: bool, -web_server: if (!builtin.single_threaded) ?WebServer else ?noreturn, +web_server: ?*AvoidableWebServer, /// Allocated into `gpa`. memory_blocked_steps: std.ArrayList(Configuration.Step.Index), /// Allocated into `gpa`. @@ -68,6 +68,8 @@ var stdio_buffer_allocation: [256]u8 = undefined; var stdout_writer_allocation: Io.File.Writer = undefined; var debug_maker_leaks: bool = false; +const AvoidableWebServer = if (builtin.single_threaded) void else WebServer; + const is_debug_mode = builtin.mode == .Debug; const use_safe_allocator = switch (builtin.mode) { .Debug, .ReleaseSafe => true, @@ -106,6 +108,7 @@ const ErrorStyle = enum { }; const MultilineErrors = enum { indent, newline, none }; const Summary = enum { all, new, failures, line, none }; +const PrintConfiguration = enum { none, zon, path }; /// Used to build the -M flags to pass to build-exe. pub const CliModule = struct { @@ -195,7 +198,7 @@ pub fn main(init: process.Init.Minimal) !void { var step_names: std.ArrayList([]const u8) = .empty; var help_menu = false; var steps_menu = false; - var print_configuration: enum { none, zon, path } = .none; + 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; @@ -546,6 +549,9 @@ pub fn main(init: process.Init.Minimal) !void { } } + 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); + const cwd_path = std.zig.getResolvedCwd(io, arena) catch |err| fatal("resolving current directory path failed: {t}", .{err}); @@ -593,585 +599,21 @@ pub fn main(init: process.Init.Minimal) !void { .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 scanned_config: ScannedConfig = sc: { - // Cache lookup for configure options. If we get a match, we can skip - // execution of the configure script. If not, we get the file path to pass - // to the configure process. - // - // In the hot path, we only check this cache, which means that also - // configure source files need to go in here. - var config_man = graph.cache.obtain(); - defer config_man.deinit(); - - for (cached_passthru_configure.items) |i| - config_man.hash.addBytes(configure_argv.items[i]); - - // Prevents a `zig build` from getting a false positive cache hit following - // a `zig build --cache-poison=ignored`. - config_man.hash.add(cache_poison == .ignored); - - 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", - }; - - configure_argv.items[conf_argv_index_build_root] = build_root.directory.path orelse cwd_path; - - var http_client: std.http.Client = .{ .allocator = gpa, .io = io }; - defer http_client.deinit(); - - var unlazy_set: Package.Fetch.JobQueue.UnlazySet = .{}; - var fork_set: Package.Fetch.JobQueue.ForkSet = .{}; - - { - // Populate fork_set. - var group: Io.Group = .init; - defer group.cancel(io); - - for (forks.items) |*fork| - group.async(io, Fork.load, .{ io, gpa, fork, color }); - - try group.await(io); - - for (forks.items) |*fork| { - if (fork.failed) process.exit(1); - try fork_set.put(arena, .{ - .path = fork.path, - .manifest_ast = fork.manifest_ast, - .manifest = fork.manifest, - .uses = 0, - }, {}); - } - } - defer Fork.deinitList(forks.items); - - var build_configurer_argv: std.ArrayList([]const u8) = .empty; - defer build_configurer_argv.deinit(gpa); - - var dependencies_source: std.ArrayList(u8) = .empty; - defer dependencies_source.deinit(gpa); - - const configurer_root_src_path: Cache.Path = .{ - .root_dir = graph.zig_lib_directory, - .sub_path = "compiler/configurer.zig", - }; - - const root_build_src_path: Cache.Path = .{ - .root_dir = build_root.directory, - .sub_path = build_root.build_zig_basename, - }; - - const configurer_exe_name = "configurer"; - - try build_configurer_argv.appendSlice(gpa, &.{ - graph.zig_exe, "build-exe", // - "--cache-dir", graph.local_cache_root.path orelse ".", // - "--global-cache-dir", graph.global_cache_root.path orelse ".", // - "--zig-lib-dir", graph.zig_lib_directory.path orelse ".", // - "--name", configurer_exe_name, // - "-fsingle-threaded", // - }); - - // Normally the build runner is compiled for the host target but here is - // some code to help when debugging edits to the build runner so that you - // can make sure it compiles successfully on other targets. - const target_arch_os_abi: ?[]const u8 = if (debug_target) |triple| t: { - config_man.hash.addBytes(triple); - try build_configurer_argv.appendSlice(gpa, &.{ "-target", triple }); - break :t triple; - } else null; - - if (graph.libc_file) |libc_file| { - try build_configurer_argv.appendSlice(gpa, &.{ "--libc", libc_file }); - } - if (graph.reference_trace) |n| { - try build_configurer_argv.append(gpa, try allocPrint(arena, "-freference-trace={d}", .{n})); - } - if (graph.debug_compile_errors) { - try build_configurer_argv.append(gpa, "--debug-compile-errors"); - } - try build_configurer_argv.appendSlice(gpa, &.{ - "--dep", "@build", // - "--dep", "@dependencies", // - try allocPrint(arena, "-Mroot={f}", .{configurer_root_src_path}), // - }); - - // In the loop below, after doing the fetch operation, the argv will be - // truncated at this point, dependencies added, and then the - // "--listen=-" arg appended at the end. - const argv_deps_index = build_configurer_argv.items.len; - - const build_mod = try arena.create(CliModule); - build_mod.* = .{ - .name = "@build", - .root_path = try root_build_src_path.toString(arena), - }; - - const deps_mod = try arena.create(CliModule); - deps_mod.* = .{ - .name = "@dependencies", - .root_path = undefined, - }; - - // This loop is re-evaluated when the build script exits with an indication that it - // could not continue due to missing lazy dependencies. - const configuration_path: Path, const poisoned: bool = cp: while (true) { - build_mod.deps.clearRetainingCapacity(); - deps_mod.deps.clearRetainingCapacity(); - - // We want to release all the locks before executing the child process, so we make a nice - // big block here to ensure the cleanup gets run when we extract out our argv. - { - { - const fetch_prog_node = main_progress_node.start("Fetch Packages", 0); - defer fetch_prog_node.end(); - - // Reset fork match counts. - for (fork_set.keys()) |*fork| fork.uses = 0; - - var job_queue: Package.Fetch.JobQueue = .{ - .io = io, - .http_client = &http_client, - .global_cache = graph.global_cache_root, - .local_storage = &.{ - .cache_root = .{ .root_dir = graph.local_cache_root }, - .pkg_root = pkg_root, - }, - .recursive = true, - .debug_hash = false, - .unlazy_set = unlazy_set, - .fork_set = fork_set, - .mode = fetch_mode, - .prog_node = fetch_prog_node, - .read_only = system_pkg_dir_path != null, - }; - defer job_queue.deinit(); - - if (system_pkg_dir_path == null) { - try http_client.initDefaultProxies(arena, &graph.environ_map); - } - - try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1); - try job_queue.table.ensureUnusedCapacity(gpa, 1); - - const phantom_package_root: Cache.Path = .{ .root_dir = build_root.directory }; - - var fetch: Package.Fetch = .{ - .arena = std.heap.ArenaAllocator.init(gpa), - .location = .{ .relative_path = phantom_package_root }, - .location_tok = 0, - .hash_tok = .none, - .name_tok = 0, - .lazy_status = .eager, - .remote_package_root = phantom_package_root, - .parent_package_root = phantom_package_root, - .parent_manifest_ast = null, - .prog_node = fetch_prog_node, - .job_queue = &job_queue, - .omit_missing_hash_error = true, - .allow_missing_paths_field = false, - .use_latest_commit = false, - - .package_root = undefined, - .error_bundle = undefined, - .manifest = undefined, - .manifest_ast = undefined, - .have_manifest = false, - .computed_hash = undefined, - .has_build_zig = true, - .oom_flag = false, - .latest_commit = null, - - .cli_module = build_mod, - }; - - job_queue.all_fetches.appendAssumeCapacity(&fetch); - - job_queue.table.putAssumeCapacityNoClobber( - Package.Fetch.relativePathDigest(phantom_package_root, graph.global_cache_root), - &fetch, - ); - - job_queue.group.async(io, Package.Fetch.workerRun, .{ &fetch, "root" }); - try job_queue.group.await(io); - - { - // Ensure that forks were actually used. This is done - // before printing manifest errors because using a fork can - // prevent them. - var any_unused = false; - for (fork_set.keys()) |*fork| { - if (fork.uses == 0) { - log.err("fork {f} matched no {s} packages", .{ - fork.path, fork.manifest.name, - }); - any_unused = true; - } else { - log.info("fork {f} matched {d} {s} packages", .{ - fork.path, fork.uses, fork.manifest.name, - }); - } - } - if (any_unused) process.exit(1); - } - - try job_queue.consolidateErrors(); - - if (fetch.error_bundle.root_list.items.len > 0) { - var errors = try fetch.error_bundle.toOwnedBundle(""); - // TODO when watching, watch and rebuild configure script rather than exit here - errors.renderToStderr(io, .{}, color) catch {}; - process.exit(1); - } - - if (fetch_only) return process.cleanExit(io); - - // Create the dependencies.zig file for configurer to - // obtain via `@import("@dependencies")`. - { - { - dependencies_source.clearRetainingCapacity(); - var source_writer: Io.Writer.Allocating = .fromArrayList(gpa, &dependencies_source); - defer dependencies_source = source_writer.toArrayList(); - job_queue.createDependenciesSource(&source_writer.writer) catch |err| switch (err) { - error.WriteFailed => return error.OutOfMemory, - }; - } - // Atomically create the file in a directory named after the hash of its contents. - var hh: Cache.HashHelper = .{}; - hh.addBytes(builtin.zig_version_string); - hh.addBytes(dependencies_source.items); - const hex_digest = hh.final(); - const dependencies_zig_path: Path = .{ - .root_dir = graph.local_cache_root, - .sub_path = try allocPrint(arena, "o/{s}/dependencies.zig", .{&hex_digest}), - }; - var atomic_file = try dependencies_zig_path.root_dir.handle.createFileAtomic( - io, - dependencies_zig_path.sub_path, - .{ .make_path = true, .replace = true }, - ); - defer atomic_file.deinit(io); - atomic_file.file.writeStreamingAll(io, dependencies_source.items) catch |err| - fatal("writing dependencies.zig contents: {t}", .{err}); - atomic_file.replace(io) catch |err| - fatal("replacing {f}: {t}", .{ dependencies_zig_path, err }); - - deps_mod.root_path = try dependencies_zig_path.toString(arena); - } - - { - // Add a CliModule for each package's build.zig. - const hashes = job_queue.table.keys(); - const fetches = job_queue.table.values(); - try deps_mod.deps.ensureUnusedCapacity(arena, @intCast(hashes.len)); - for (hashes, fetches) |*hash, f| { - if (f == &fetch) { - // The first one is a dummy package for the current project. - continue; - } - if (!f.has_build_zig) - continue; - const hash_slice = try arena.dupe(u8, hash.toSlice()); - - const m = try arena.create(CliModule); - m.* = .{ - .root_path = try f.package_root.toString(arena), - .name = hash_slice, - }; - deps_mod.deps.putAssumeCapacityNoClobber(hash_slice, m); - f.cli_module = m; - } - - // Each build.zig module needs access to each of its - // dependencies' build.zig modules by name. - for (fetches) |f| { - const mod = f.cli_module orelse continue; - if (!f.have_manifest) continue; - const man = &f.manifest; - const dep_names = man.dependencies.keys(); - try mod.deps.ensureUnusedCapacity(arena, @intCast(dep_names.len)); - for (dep_names, man.dependencies.values()) |name, dep| { - const dep_digest = Package.Fetch.depDigest( - f.package_root, - global_cache_directory, - dep, - ) orelse continue; - const dep_mod = job_queue.table.get(dep_digest).?.cli_module orelse continue; - const name_cloned = try arena.dupe(u8, name); - mod.deps.putAssumeCapacityNoClobber(name_cloned, dep_mod); - } - } - } - - // Lower module dependencies to CLI argv. - build_configurer_argv.shrinkRetainingCapacity(argv_deps_index); - for (deps_mod.deps.values()) |dep| { - try build_configurer_argv.ensureUnusedCapacity(gpa, 2 * dep.deps.count() + 1); - for (dep.deps.keys(), dep.deps.values()) |name, sub| { - build_configurer_argv.appendAssumeCapacity("--dep"); - if (mem.eql(u8, name, sub.name)) { - build_configurer_argv.appendAssumeCapacity(sub.name); - } else { - build_configurer_argv.appendAssumeCapacity(try allocPrint(arena, "{s}={s}", .{ - name, sub.name, - })); - } - } - build_configurer_argv.appendAssumeCapacity(try allocPrint(arena, "-M{s}={s}/{s}", .{ - dep.name, dep.root_path, std.zig.build_zig_basename, - })); - } - try deps_mod.lower(arena, gpa, &build_configurer_argv); - try build_mod.lower(arena, gpa, &build_configurer_argv); - - try build_configurer_argv.append(gpa, "--listen=-"); - } - - const compile_prog_node = main_progress_node.start("Compile Configure Script", 0); - defer compile_prog_node.end(); - - switch (cache_poison) { - .pure, .disallowed, .ignored => if (try config_man.hit()) { - const digest = config_man.final(); - break :cp .{ - .{ - .root_dir = graph.local_cache_root, - .sub_path = try allocPrint(arena, "c/{s}", .{&digest}), - }, - false, - }; - }, - .poisoned => {}, // Don't bother checking for cache hit. - } - - const configure_exe_path: Path = if (std.zig.buildExeSubprocess(gpa, io, .{ - .argv = build_configurer_argv.items, - .cache_root = graph.local_cache_root, - .root_name = configurer_exe_name, - .environ_map = &graph.environ_map, - .cache_manifest = &config_man, - .arch_os_abi = target_arch_os_abi, - .progress_node = compile_prog_node, - })) |r| r.path else |err| switch (err) { - error.AlreadyReported => process.exit(1), - // If the file system inputs are populated, we can - // still watch for changes and try again. - error.FailedButCacheIntact => @panic("TODO"), - error.Canceled, error.OutOfMemory => |e| return e, - }; - defer gpa.free(configure_exe_path.sub_path); - - configure_argv.items[0] = try configure_exe_path.toString(arena); - } - - if (!process.can_spawn) { - fatal("cannot spawn command on {t}: {f}", .{ native_os, @as(std.zig.SubprocessCommand, .{ - .argv = configure_argv.items, - }) }); - } - - const rand_int = randInt(io, u64); - const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); - const config_tmp_path: Path = .{ - .root_dir = graph.local_cache_root, - .sub_path = tmp_dir_sub_path, - }; - const config_tmp_file: Io.File = try config_tmp_path.root_dir.handle.createFile( - io, - config_tmp_path.sub_path, - .{ .read = true, .exclusive = true }, - ); - defer config_tmp_file.close(io); - - const term = term: { - const child_node = main_progress_node.start("Run Configure Script", 0); - defer child_node.end(); - var child = process.spawn(io, .{ - .argv = configure_argv.items, - .stdout = .{ .file = config_tmp_file }, - .progress_node = child_node, - }) catch |err| fatal("failed to spawn configure script {q}: {t}", .{ configure_argv.items[0], err }); - defer child.kill(io); - break :term child.wait(io) catch |err| - fatal("failed to wait configure script {q}: {t}", .{ configure_argv.items[0], err }); - }; - if (!term.success()) { - // Failure to produce the configuration file. - fatal("configure command {f}: {f}", .{ term, @as(std.zig.SubprocessCommand, .{ - .argv = configure_argv.items, - }) }); - } - // Even though the file is designed to be sent directly to make - // runner, we must load it now because: - // * If it contains additional file dependencies, we need to - // add them to `config_man` before obtaining the final digest. - // * If it contains a set of lazy packages that need to be - // fetched, we need to fetch those now and re-run configure. - var configuration = Configuration.loadFile(arena, io, config_tmp_file) catch |err| - fatal("failed to load configuration file {f}: {t}", .{ config_tmp_path, err }); - - if (configuration.unlazy_deps.len != 0) { - var any_errors = false; - for (configuration.unlazy_deps) |hash_string| { - const hash = hash_string.slice(&configuration); - assert(hash.len != 0); - if (hash.len > Package.Hash.max_len) { - log.err("invalid digest (length {d} exceeds maximum): {q}", .{ hash.len, hash }); - any_errors = true; - continue; - } - try unlazy_set.put(arena, .fromSlice(hash), {}); - } - if (any_errors) process.exit(1); - if (system_pkg_dir_path) |p| { - // In this mode, the system needs to provide these packages; they - // cannot be fetched by Zig. - const s = Dir.path.sep_str; - for (unlazy_set.keys()) |*hash| { - log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ p, hash.toSlice() }); - } - log.info("remote package fetching disabled due to --system mode", .{}); - log.info("dependencies might be avoidable depending on build configuration", .{}); - process.exit(1); - } - continue :cp; - } - - for (configuration.path_deps) |path_dep| { - try config_man.addPathPost(path_dep.toCachePath(&configuration, arena)); - } - - // If it is poisoned, there is no point in moving it to cached - // location. Just leave it in the tmp directory. - if (configuration.poisoned) { - break :cp .{ config_tmp_path, true }; - } else { - const digest = config_man.final(); - const final_path: Path = .{ - .root_dir = graph.local_cache_root, - .sub_path = try allocPrint(arena, "c/{s}", .{&digest}), - }; - Io.Dir.rename( - config_tmp_path.root_dir.handle, - config_tmp_path.sub_path, - final_path.root_dir.handle, - final_path.sub_path, - io, - ) catch |err| retry: { - const e = switch (err) { - error.FileNotFound => e: { - const dir_path = final_path.dirname().?; - dir_path.root_dir.handle.createDirPath(io, dir_path.sub_path) catch |e| - fatal("failed to create directory {f}: {t}", .{ dir_path, e }); - if (Io.Dir.rename( - config_tmp_path.root_dir.handle, - config_tmp_path.sub_path, - final_path.root_dir.handle, - final_path.sub_path, - io, - )) |_| break :retry else |e| break :e e; - }, - else => |e| e, - }; - fatal("failed to rename configuration file from {f} into {f}: {t}", .{ - config_tmp_path, final_path, e, - }); - }; - config_man.writeManifest() catch |err| log.warn("failed to write cache manifest: {t}", .{err}); - break :cp .{ final_path, false }; - } - }; - - // Hang on to the configuration file lock until we finish loading the configuration file. - var configuration_lock = if (!poisoned) config_man.toOwnedLock() else null; - defer if (configuration_lock) |*l| l.release(io); - - switch (print_configuration) { - .path => { - initStdoutWriter(io).print("{f}\n", .{configuration_path}) catch - fatal("failed printing cache file path: {t}", .{stdout_writer_allocation.err.?}); - stdout_writer_allocation.flush() catch |err| - fatal("failed printing cache file path: {t}", .{err}); - return process.cleanExit(io); - }, - .none, .zon => {}, - } - - const configuration = c: { - var file = configuration_path.root_dir.handle.openFile(io, configuration_path.sub_path, .{}) catch |err| - fatal("failed to open configuration file {f}: {t}", .{ configuration_path, err }); - defer file.close(io); - break :c Configuration.loadFile(arena, io, file) catch |err| - fatal("failed to load configuration file {f}: {t}", .{ configuration_path, err }); - }; - // Technically if the configuration is marked as poisoned, we could - // already delete the file now, but we leave it around in case the - // maker process fails or crashes and it's helpful to be able to repeat - // execution of the command line or otherwise inspect the configuration file. - const c = &configuration; - var top_level_steps: std.array_hash_map.String(Configuration.Step.Index) = .empty; - for (configuration.steps, 0..) |*conf_step, step_index_usize| { - if (conf_step.owner != .root) continue; - const step_index: Configuration.Step.Index = @enumFromInt(step_index_usize); - const flags = conf_step.flags(c); - switch (flags.tag) { - .top_level => { - const name = step_index.ptr(c).name.slice(c); - try top_level_steps.put(arena, name, step_index); - }, - else => {}, - } - } - for (c.search_prefixes) |search_prefix| { - try graph.search_prefixes.append(arena, search_prefix.slice(c)); - } - break :sc .{ - .configuration = configuration, - .top_level_steps = top_level_steps, - .path = configuration_path, - }; - }; - - 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, - } - - if (webui_listen != null) { - if (watch) fatal("using '--webui' and '--watch' together is not yet supported; consider omitting '--watch' in favour of the web UI \"Rebuild\" button", .{}); - if (builtin.single_threaded) fatal("'--webui' is not yet supported on single-threaded hosts", .{}); - } - 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" }), @@ -1198,147 +640,768 @@ pub fn main(init: process.Init.Minimal) !void { .sub_path = cwd_relative, } else try install_prefix_path.join(arena, "include"); - 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 = undefined, // set after `prepare` - .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 => { - _ = 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); - }; - const now = Io.Clock.Timestamp.now(io, .awake); - maker.web_server = if (webui_listen) |listen_address| ws: { - if (builtin.single_threaded) unreachable; // `fatal` above - break :ws .init(.{ - .maker = &maker, + 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; - if (maker.web_server) |*ws| { - ws.start() catch |err| fatal("failed to start web server: {t}", .{err}); + while (true) { + // If this fails, we can still start the server and wait for user + // 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 = undefined, // set after `prepare` + .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 => { + _ = 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); // fatal error after CLI parsing + 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; + + // Comptime-known guard to prevent including the logic below when `!Watch.have_impl`. + if (!Watch.have_impl) unreachable; + + try w.update(maker.step_stack.keys()); + + // Wait until a file system notification arrives. Read all such events + // 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) process.exit(1); + if (can_fs_watch) { + @panic("TODO set up fs watching"); + } else { + @panic("TODO wait for user to request rebuild"); + } + } } +} - 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 (maker.web_server) |*ws| ws.startBuild(); +const ConfigureOptions = struct { + configure_argv: [][]const u8, + conf_argv_index_build_root: usize, + cached_passthru_configure: []const u32, + + cache_poison: std.Build.Graph.CachePoison, + pkg_root: Path, + build_root: BuildRoot, + cwd_path: []const u8, + color: Color, + debug_target: ?[]const u8, + parent_progress_node: std.Progress.Node, + fetch_mode: Fetch.JobQueue.Mode, + system_pkg_dir_path: ?[]const u8, + fetch_only: bool, + print_configuration: PrintConfiguration, + forks: []Fork, +}; + +fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { + const configure_argv = options.configure_argv; + const gpa = graph.cache.gpa; + const io = graph.io; + const arena = graph.arena; + + // Cache lookup for configure options. If we get a match, we can skip + // execution of the configure script. If not, we get the file path to pass + // to the configure process. + // + // In the hot path, we only check this cache, which means that also + // configure source files need to go in here. + var config_man = graph.cache.obtain(); + defer config_man.deinit(); + + for (options.cached_passthru_configure) |i| + config_man.hash.addBytes(configure_argv[i]); + + // Prevents a `zig build` from getting a false positive cache hit following + // a `zig build --cache-poison=ignored`. + config_man.hash.add(options.cache_poison == .ignored); - try maker.makeStepNames(step_names.items, main_progress_node, fuzz); + configure_argv[options.conf_argv_index_build_root] = options.build_root.directory.path orelse options.cwd_path; - if (maker.web_server) |*web_server| { - if (fuzz) |mode| if (mode != .forever) fatal( - "error: limited fuzzing is not implemented yet for --webui", - .{}, - ); + var http_client: std.http.Client = .{ .allocator = gpa, .io = io }; + defer http_client.deinit(); - web_server.finishBuild(.{ .fuzz = fuzz != null }); + var unlazy_set: Package.Fetch.JobQueue.UnlazySet = .{}; + var fork_set: Package.Fetch.JobQueue.ForkSet = .{}; + + { + // Populate fork_set. + var group: Io.Group = .init; + defer group.cancel(io); + + for (options.forks) |*fork| + group.async(io, Fork.load, .{ io, gpa, fork, options.color }); + + try group.await(io); + + for (options.forks) |*fork| { + if (fork.failed) process.exit(1); + try fork_set.put(arena, .{ + .path = fork.path, + .manifest_ast = fork.manifest_ast, + .manifest = fork.manifest, + .uses = 0, + }, {}); } + } + defer Fork.deinitList(options.forks); + + var build_configurer_argv: std.ArrayList([]const u8) = .empty; + defer build_configurer_argv.deinit(gpa); + + var dependencies_source: std.ArrayList(u8) = .empty; + defer dependencies_source.deinit(gpa); + + const configurer_root_src_path: Cache.Path = .{ + .root_dir = graph.zig_lib_directory, + .sub_path = "compiler/configurer.zig", + }; + + const root_build_src_path: Cache.Path = .{ + .root_dir = options.build_root.directory, + .sub_path = options.build_root.build_zig_basename, + }; + + const configurer_exe_name = "configurer"; + + try build_configurer_argv.appendSlice(gpa, &.{ + graph.zig_exe, "build-exe", // + "--cache-dir", graph.local_cache_root.path orelse ".", // + "--global-cache-dir", graph.global_cache_root.path orelse ".", // + "--zig-lib-dir", graph.zig_lib_directory.path orelse ".", // + "--name", configurer_exe_name, // + "-fsingle-threaded", // + }); + + // Normally the build runner is compiled for the host target but here is + // some code to help when debugging edits to the build runner so that you + // can make sure it compiles successfully on other targets. + const target_arch_os_abi: ?[]const u8 = if (options.debug_target) |triple| t: { + config_man.hash.addBytes(triple); + try build_configurer_argv.appendSlice(gpa, &.{ "-target", triple }); + break :t triple; + } else null; + + if (graph.libc_file) |libc_file| { + try build_configurer_argv.appendSlice(gpa, &.{ "--libc", libc_file }); + } + if (graph.reference_trace) |n| { + try build_configurer_argv.append(gpa, try allocPrint(arena, "-freference-trace={d}", .{n})); + } + if (graph.debug_compile_errors) { + try build_configurer_argv.append(gpa, "--debug-compile-errors"); + } + try build_configurer_argv.appendSlice(gpa, &.{ + "--dep", "@build", // + "--dep", "@dependencies", // + try allocPrint(arena, "-Mroot={f}", .{configurer_root_src_path}), // + }); + + // In the loop below, after doing the fetch operation, the argv will be + // truncated at this point, dependencies added, and then the + // "--listen=-" arg appended at the end. + const argv_deps_index = build_configurer_argv.items.len; + + const build_mod = try arena.create(CliModule); + build_mod.* = .{ + .name = "@build", + .root_path = try root_build_src_path.toString(arena), + }; - if (maker.web_server) |*web_server| { - const c = &scanned_config.configuration; - assert(!watch); // fatal error after CLI parsing - while (true) switch (try web_server.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); + const deps_mod = try arena.create(CliModule); + deps_mod.* = .{ + .name = "@dependencies", + .root_path = undefined, + }; + + // This loop is re-evaluated when the build script exits with an indication that it + // could not continue due to missing lazy dependencies. + const configuration_path: Path, const poisoned: bool = cp: while (true) { + build_mod.deps.clearRetainingCapacity(); + deps_mod.deps.clearRetainingCapacity(); + + // We want to release all the locks before executing the child process, so we make a nice + // big block here to ensure the cleanup gets run when we extract out our argv. + { + { + const fetch_prog_node = options.parent_progress_node.start("Fetch Packages", 0); + defer fetch_prog_node.end(); + + // Reset fork match counts. + for (fork_set.keys()) |*fork| fork.uses = 0; + + var job_queue: Package.Fetch.JobQueue = .{ + .io = io, + .http_client = &http_client, + .global_cache = graph.global_cache_root, + .local_storage = &.{ + .cache_root = .{ .root_dir = graph.local_cache_root }, + .pkg_root = options.pkg_root, + }, + .recursive = true, + .debug_hash = false, + .unlazy_set = unlazy_set, + .fork_set = fork_set, + .mode = options.fetch_mode, + .prog_node = fetch_prog_node, + .read_only = options.system_pkg_dir_path != null, + }; + defer job_queue.deinit(); + + if (options.system_pkg_dir_path == null) { + try http_client.initDefaultProxies(arena, &graph.environ_map); + } + + try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1); + try job_queue.table.ensureUnusedCapacity(gpa, 1); + + const phantom_package_root: Cache.Path = .{ .root_dir = options.build_root.directory }; + + var fetch: Package.Fetch = .{ + .arena = std.heap.ArenaAllocator.init(gpa), + .location = .{ .relative_path = phantom_package_root }, + .location_tok = 0, + .hash_tok = .none, + .name_tok = 0, + .lazy_status = .eager, + .remote_package_root = phantom_package_root, + .parent_package_root = phantom_package_root, + .parent_manifest_ast = null, + .prog_node = fetch_prog_node, + .job_queue = &job_queue, + .omit_missing_hash_error = true, + .allow_missing_paths_field = false, + .use_latest_commit = false, + + .package_root = undefined, + .error_bundle = undefined, + .manifest = undefined, + .manifest_ast = undefined, + .have_manifest = false, + .computed_hash = undefined, + .has_build_zig = true, + .oom_flag = false, + .latest_commit = null, + + .cli_module = build_mod, + }; + + job_queue.all_fetches.appendAssumeCapacity(&fetch); + + job_queue.table.putAssumeCapacityNoClobber( + Package.Fetch.relativePathDigest(phantom_package_root, graph.global_cache_root), + &fetch, + ); + + job_queue.group.async(io, Package.Fetch.workerRun, .{ &fetch, "root" }); + try job_queue.group.await(io); + + { + // Ensure that forks were actually used. This is done + // before printing manifest errors because using a fork can + // prevent them. + var any_unused = false; + for (fork_set.keys()) |*fork| { + if (fork.uses == 0) { + log.err("fork {f} matched no {s} packages", .{ + fork.path, fork.manifest.name, + }); + any_unused = true; + } else { + log.info("fork {f} matched {d} {s} packages", .{ + fork.path, fork.uses, fork.manifest.name, + }); + } + } + if (any_unused) process.exit(1); + } + + try job_queue.consolidateErrors(); + + if (fetch.error_bundle.root_list.items.len > 0) { + var errors = try fetch.error_bundle.toOwnedBundle(""); + errors.renderToStderr(io, .{}, options.color) catch process.exit(1); + return error.FailedButCacheIntact; + } + + if (options.fetch_only) { + _ = io.lockStderr(&.{}, .no_color) catch {}; + process.exit(0); + } + + // Create the dependencies.zig file for configurer to + // obtain via `@import("@dependencies")`. + { + { + dependencies_source.clearRetainingCapacity(); + var source_writer: Io.Writer.Allocating = .fromArrayList(gpa, &dependencies_source); + defer dependencies_source = source_writer.toArrayList(); + job_queue.createDependenciesSource(&source_writer.writer) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + }; + } + // Atomically create the file in a directory named after the hash of its contents. + var hh: Cache.HashHelper = .{}; + hh.addBytes(builtin.zig_version_string); + hh.addBytes(dependencies_source.items); + const hex_digest = hh.final(); + const dependencies_zig_path: Path = .{ + .root_dir = graph.local_cache_root, + .sub_path = try allocPrint(arena, "o/{s}/dependencies.zig", .{&hex_digest}), + }; + var atomic_file = try dependencies_zig_path.root_dir.handle.createFileAtomic( + io, + dependencies_zig_path.sub_path, + .{ .make_path = true, .replace = true }, + ); + defer atomic_file.deinit(io); + atomic_file.file.writeStreamingAll(io, dependencies_source.items) catch |err| + fatal("writing dependencies.zig contents: {t}", .{err}); + atomic_file.replace(io) catch |err| + fatal("replacing {f}: {t}", .{ dependencies_zig_path, err }); + + deps_mod.root_path = try dependencies_zig_path.toString(arena); + } + + { + // Add a CliModule for each package's build.zig. + const hashes = job_queue.table.keys(); + const fetches = job_queue.table.values(); + try deps_mod.deps.ensureUnusedCapacity(arena, @intCast(hashes.len)); + for (hashes, fetches) |*hash, f| { + if (f == &fetch) { + // The first one is a dummy package for the current project. + continue; + } + if (!f.has_build_zig) + continue; + const hash_slice = try arena.dupe(u8, hash.toSlice()); + + const m = try arena.create(CliModule); + m.* = .{ + .root_path = try f.package_root.toString(arena), + .name = hash_slice, + }; + deps_mod.deps.putAssumeCapacityNoClobber(hash_slice, m); + f.cli_module = m; + } + + // Each build.zig module needs access to each of its + // dependencies' build.zig modules by name. + for (fetches) |f| { + const mod = f.cli_module orelse continue; + if (!f.have_manifest) continue; + const man = &f.manifest; + const dep_names = man.dependencies.keys(); + try mod.deps.ensureUnusedCapacity(arena, @intCast(dep_names.len)); + for (dep_names, man.dependencies.values()) |name, dep| { + const dep_digest = Package.Fetch.depDigest( + f.package_root, + graph.global_cache_root, + dep, + ) orelse continue; + const dep_mod = job_queue.table.get(dep_digest).?.cli_module orelse continue; + const name_cloned = try arena.dupe(u8, name); + mod.deps.putAssumeCapacityNoClobber(name_cloned, dep_mod); + } + } + } + + // Lower module dependencies to CLI argv. + build_configurer_argv.shrinkRetainingCapacity(argv_deps_index); + for (deps_mod.deps.values()) |dep| { + try build_configurer_argv.ensureUnusedCapacity(gpa, 2 * dep.deps.count() + 1); + for (dep.deps.keys(), dep.deps.values()) |name, sub| { + build_configurer_argv.appendAssumeCapacity("--dep"); + if (mem.eql(u8, name, sub.name)) { + build_configurer_argv.appendAssumeCapacity(sub.name); + } else { + build_configurer_argv.appendAssumeCapacity(try allocPrint(arena, "{s}={s}", .{ + name, sub.name, + })); + } } - continue :rebuild; + build_configurer_argv.appendAssumeCapacity(try allocPrint(arena, "-M{s}={s}/{s}", .{ + dep.name, dep.root_path, std.zig.build_zig_basename, + })); + } + try deps_mod.lower(arena, gpa, &build_configurer_argv); + try build_mod.lower(arena, gpa, &build_configurer_argv); + + try build_configurer_argv.append(gpa, "--listen=-"); + } + + const compile_prog_node = options.parent_progress_node.start("Compile Configure Script", 0); + defer compile_prog_node.end(); + + switch (options.cache_poison) { + .pure, .disallowed, .ignored => if (try config_man.hit()) { + const digest = config_man.final(); + break :cp .{ + .{ + .root_dir = graph.local_cache_root, + .sub_path = try allocPrint(arena, "c/{s}", .{&digest}), + }, + false, + }; }, + .poisoned => {}, // Don't bother checking for cache hit. + } + + const configure_exe_path: Path = if (std.zig.buildExeSubprocess(gpa, io, .{ + .argv = build_configurer_argv.items, + .cache_root = graph.local_cache_root, + .root_name = configurer_exe_name, + .environ_map = &graph.environ_map, + .cache_manifest = &config_man, + .arch_os_abi = target_arch_os_abi, + .progress_node = compile_prog_node, + })) |r| r.path else |err| return err; + defer gpa.free(configure_exe_path.sub_path); + + configure_argv[0] = try configure_exe_path.toString(arena); + } + + if (!process.can_spawn) { + fatal("cannot spawn command on {t}: {f}", .{ native_os, @as(std.zig.SubprocessCommand, .{ + .argv = configure_argv, + }) }); + } + + const rand_int = randInt(io, u64); + const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); + const config_tmp_path: Path = .{ + .root_dir = graph.local_cache_root, + .sub_path = tmp_dir_sub_path, + }; + const config_tmp_file: Io.File = try config_tmp_path.root_dir.handle.createFile( + io, + config_tmp_path.sub_path, + .{ .read = true, .exclusive = true }, + ); + defer config_tmp_file.close(io); + + const term = term: { + const child_node = options.parent_progress_node.start("Run Configure Script", 0); + defer child_node.end(); + var child = process.spawn(io, .{ + .argv = configure_argv, + .stdout = .{ .file = config_tmp_file }, + .progress_node = child_node, + }) catch |err| fatal("failed to spawn configure script {q}: {t}", .{ configure_argv[0], err }); + defer child.kill(io); + break :term child.wait(io) catch |err| + fatal("failed to wait configure script {q}: {t}", .{ configure_argv[0], err }); + }; + if (!term.success()) { + // Failure to produce the configuration file. + fatal("configure command {f}: {f}", .{ term, @as(std.zig.SubprocessCommand, .{ + .argv = configure_argv, + }) }); + } + // Even though the file is designed to be sent directly to make + // runner, we must load it now because: + // * If it contains additional file dependencies, we need to + // add them to `config_man` before obtaining the final digest. + // * If it contains a set of lazy packages that need to be + // fetched, we need to fetch those now and re-run configure. + var configuration = Configuration.loadFile(arena, io, config_tmp_file) catch |err| + fatal("failed to load configuration file {f}: {t}", .{ config_tmp_path, err }); + + if (configuration.unlazy_deps.len != 0) { + var any_errors = false; + for (configuration.unlazy_deps) |hash_string| { + const hash = hash_string.slice(&configuration); + assert(hash.len != 0); + if (hash.len > Package.Hash.max_len) { + log.err("invalid digest (length {d} exceeds maximum): {q}", .{ hash.len, hash }); + any_errors = true; + continue; + } + try unlazy_set.put(arena, .fromSlice(hash), {}); + } + if (any_errors) process.exit(1); + if (options.system_pkg_dir_path) |p| { + // In this mode, the system needs to provide these packages; they + // cannot be fetched by Zig. + const s = Dir.path.sep_str; + for (unlazy_set.keys()) |*hash| { + log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ p, hash.toSlice() }); + } + log.info("remote package fetching disabled due to --system mode", .{}); + log.info("dependencies might be avoidable depending on build configuration", .{}); + process.exit(1); + } + continue :cp; + } + + for (configuration.path_deps) |path_dep| { + try config_man.addPathPost(path_dep.toCachePath(&configuration, arena)); + } + + // If it is poisoned, there is no point in moving it to cached + // location. Just leave it in the tmp directory. + if (configuration.poisoned) { + break :cp .{ config_tmp_path, true }; + } else { + const digest = config_man.final(); + const final_path: Path = .{ + .root_dir = graph.local_cache_root, + .sub_path = try allocPrint(arena, "c/{s}", .{&digest}), + }; + Io.Dir.rename( + config_tmp_path.root_dir.handle, + config_tmp_path.sub_path, + final_path.root_dir.handle, + final_path.sub_path, + io, + ) catch |err| retry: { + const e = switch (err) { + error.FileNotFound => e: { + const dir_path = final_path.dirname().?; + dir_path.root_dir.handle.createDirPath(io, dir_path.sub_path) catch |e| + fatal("failed to create directory {f}: {t}", .{ dir_path, e }); + if (Io.Dir.rename( + config_tmp_path.root_dir.handle, + config_tmp_path.sub_path, + final_path.root_dir.handle, + final_path.sub_path, + io, + )) |_| break :retry else |e| break :e e; + }, + else => |e| e, + }; + fatal("failed to rename configuration file from {f} into {f}: {t}", .{ + config_tmp_path, final_path, e, + }); }; + config_man.writeManifest() catch |err| log.warn("failed to write cache manifest: {t}", .{err}); + break :cp .{ final_path, false }; } + }; - if (!maker.watch) return; - - // Comptime-known guard to prevent including the logic below when `!Watch.have_impl`. - if (!Watch.have_impl) unreachable; - - try w.update(maker.step_stack.keys()); - - // Wait until a file system notification arrives. Read all such events - // 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); + // Hang on to the configuration file lock until we finish loading the configuration file. + var configuration_lock = if (!poisoned) config_man.toOwnedLock() else null; + defer if (configuration_lock) |*l| l.release(io); + + switch (options.print_configuration) { + .path => { + initStdoutWriter(io).print("{f}\n", .{configuration_path}) catch + fatal("failed printing cache file path: {t}", .{stdout_writer_allocation.err.?}); + stdout_writer_allocation.flush() catch |err| + fatal("failed printing cache file path: {t}", .{err}); + _ = io.lockStderr(&.{}, .no_color) catch {}; + process.exit(0); + }, + .none, .zon => {}, + } + + const configuration = c: { + var file = configuration_path.root_dir.handle.openFile(io, configuration_path.sub_path, .{}) catch |err| + fatal("failed to open configuration file {f}: {t}", .{ configuration_path, err }); + defer file.close(io); + break :c Configuration.loadFile(arena, io, file) catch |err| + fatal("failed to load configuration file {f}: {t}", .{ configuration_path, err }); + }; + // Technically if the configuration is marked as poisoned, we could + // already delete the file now, but we leave it around in case the + // maker process fails or crashes and it's helpful to be able to repeat + // execution of the command line or otherwise inspect the configuration file. + const c = &configuration; + var top_level_steps: std.array_hash_map.String(Configuration.Step.Index) = .empty; + for (configuration.steps, 0..) |*conf_step, step_index_usize| { + if (conf_step.owner != .root) continue; + const step_index: Configuration.Step.Index = @enumFromInt(step_index_usize); + const flags = conf_step.flags(c); + switch (flags.tag) { + .top_level => { + const name = step_index.ptr(c).name.slice(c); + try top_level_steps.put(arena, name, step_index); }, - .clean => {}, - }; + else => {}, + } + } + for (c.search_prefixes) |search_prefix| { + try graph.search_prefixes.append(arena, search_prefix.slice(c)); } + return .{ + .configuration = configuration, + .top_level_steps = top_level_steps, + .path = configuration_path, + }; } fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void { @@ -1894,7 +1957,7 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void { fn makeStepNames( maker: *Maker, step_names: []const []const u8, - parent_prog_node: std.Progress.Node, + parent_progress_node: std.Progress.Node, fuzz: ?Fuzz.Mode, ) !void { const graph = maker.graph; @@ -1918,7 +1981,7 @@ fn makeStepNames( } } - const step_prog = parent_prog_node.start("steps", step_stack.count()); + const step_prog = parent_progress_node.start("steps", step_stack.count()); defer step_prog.end(); var group: Io.Group = .init; @@ -1998,7 +2061,7 @@ fn makeStepNames( } assert(mode == .limit); - var f = Fuzz.init(maker, step_stack.keys(), parent_prog_node, mode) catch |err| + var f = Fuzz.init(maker, step_stack.keys(), parent_progress_node, mode) catch |err| fatal("failed to start fuzzer: {t}", .{err}); defer f.deinit(); @@ -2189,7 +2252,7 @@ fn makeStep( const step_prog_node = root_prog_node.start(step_name, 0); defer step_prog_node.end(); - if (maker.web_server) |*ws| ws.updateStepStatus(step_index, .wip); + if (maker.web_server) |ws| ws.updateStepStatus(step_index, .wip); const new_state: Step.State = for (deps) |dep_index| { const dep_make_step = maker.stepByIndex(dep_index); @@ -2224,14 +2287,14 @@ fn makeStep( .dependency_failure, .skipped_oom, => { - if (maker.web_server) |*ws| ws.updateStepStatus(step_index, .failure); + if (maker.web_server) |ws| ws.updateStepStatus(step_index, .failure); std.Progress.setStatus(.failure_working); }, .success, .skipped, => { - if (maker.web_server) |*ws| ws.updateStepStatus(step_index, .success); + if (maker.web_server) |ws| ws.updateStepStatus(step_index, .success); }, } } diff --git a/lib/compiler/Maker/Fuzz.zig b/lib/compiler/Maker/Fuzz.zig index 44c3ccd2705799819cf189ae28053ddc9dce3583..56f773eae9e8c4644e80419d75144419a62be49f 100644 --- a/lib/compiler/Maker/Fuzz.zig +++ b/lib/compiler/Maker/Fuzz.zig @@ -166,9 +166,9 @@ pub fn deinit(fuzz: *Fuzz) void { fn rebuildTestsWorkerRun( maker: *Maker, run_index: Configuration.Step.Index, - parent_prog_node: std.Progress.Node, + parent_progress_node: std.Progress.Node, ) void { - rebuildTestsWorkerRunFallible(maker, run_index, parent_prog_node) catch |err| { + rebuildTestsWorkerRunFallible(maker, run_index, parent_progress_node) catch |err| { const conf = &maker.scanned_config.configuration; const conf_run = run_index.ptr(conf).extended.cast(conf, Configuration.Step.Run).?; const comp_index = conf_run.producer.value.?; @@ -180,7 +180,7 @@ fn rebuildTestsWorkerRun( fn rebuildTestsWorkerRunFallible( maker: *Maker, run_index: Configuration.Step.Index, - parent_prog_node: std.Progress.Node, + parent_progress_node: std.Progress.Node, ) !void { const graph = maker.graph; const io = graph.io; @@ -196,7 +196,7 @@ fn rebuildTestsWorkerRunFallible( const root_module = conf_comp.root_module.get(conf); const target = root_module.resolved_target.get(conf).?.result.get(conf); - const prog_node = parent_prog_node.start(conf_comp_step.name.slice(conf), 0); + const prog_node = parent_progress_node.start(conf_comp_step.name.slice(conf), 0); defer prog_node.end(); const result = comp.rebuildInFuzzMode(maker, comp_index, prog_node); diff --git a/lib/compiler/Maker/Step.zig b/lib/compiler/Maker/Step.zig index a8f0fdb3425c6396530b4a1fe30e2fce81c56131..8ccb1415d8f17a1df24dfa9505d0ac699304e97f 100644 --- a/lib/compiler/Maker/Step.zig +++ b/lib/compiler/Maker/Step.zig @@ -654,7 +654,7 @@ fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *Zi } } }, - .time_report => if (maker.web_server) |*ws| { + .time_report => if (maker.web_server) |ws| { const TimeReport = std.zig.Server.Message.TimeReport; const tr: *align(1) const TimeReport = @ptrCast(body[0..@sizeOf(TimeReport)]); ws.updateTimeReportCompile(.{ diff --git a/lib/compiler/Maker/Step/Run.zig b/lib/compiler/Maker/Step/Run.zig index 9ca0b0071ef85554d51f6e2ee328f7bd93d29760..f95ef1ce08979b056acdcfd350ac9aef64c7a958 100644 --- a/lib/compiler/Maker/Step/Run.zig +++ b/lib/compiler/Maker/Step/Run.zig @@ -1242,7 +1242,7 @@ fn evalZigTest( step.test_results = test_results; if (test_metadata) |tm| { run.cached_test_metadata = tm.toCachedTestMetadata(); - if (maker.web_server) |*ws| { + if (maker.web_server) |ws| { if (graph.time_report) { ws.updateTimeReportRunTest( run_index, diff --git a/lib/compiler/Maker/WebServer.zig b/lib/compiler/Maker/WebServer.zig index 7a8c5013611c196ede9c3e6390e2fa9989c41eab..f77c367f6103724c7990a05c0e3be94a38ecf736 100644 --- a/lib/compiler/Maker/WebServer.zig +++ b/lib/compiler/Maker/WebServer.zig @@ -19,7 +19,7 @@ const Fuzz = @import("Fuzz.zig"); const Graph = @import("Graph.zig"); const Step = @import("Step.zig"); -maker: *Maker, +graph: *const Graph, listen_address: net.IpAddress, root_prog_node: std.Progress.Node, @@ -28,17 +28,8 @@ serve_task: ?Io.Future(Io.Cancelable!void), /// Uses `Io.Clock.awake`. base_timestamp: Io.Timestamp, -/// The "step name" data which trails `abi.Hello`, for the steps in `all_steps`. -step_names_trailing: []u8, - -/// The bit-packed "step status" data. Values are `abi.StepUpdate.Status`. LSBs are earlier steps. -/// Accessed atomically. -step_status_bits: []u8, fuzz: ?Fuzz, -time_report_mutex: Io.Mutex, -time_report_msgs: [][]u8, -time_report_update_times: []i64, build_status: std.atomic.Value(abi.BuildStatus), /// When an event occurs which means WebSocket clients should be sent updates, call `notifyUpdate` @@ -55,6 +46,21 @@ runner_request_ready_cond: Io.Condition, runner_request_empty_cond: Io.Condition, runner_request: ?RunnerRequest, +configured: ?Configured, + +const Configured = struct { + maker: *Maker, + /// The "step name" data which trails `abi.Hello`, for the steps in `all_steps`. + step_names_trailing: []u8, + /// The bit-packed "step status" data. Values are `abi.StepUpdate.Status`. LSBs are earlier steps. + /// Accessed atomically. + step_status_bits: []u8, + + time_report_mutex: Io.Mutex, + time_report_msgs: [][]u8, + time_report_update_times: []i64, +}; + /// If a client is not explicitly notified of changes with `notifyUpdate`, it will be sent updates /// on a fixed interval of this many milliseconds. const default_update_interval_ms = 500; @@ -63,34 +69,88 @@ pub const base_clock: Io.Clock = .awake; /// Thread-safe. Triggers updates to be sent to connected WebSocket clients; see `update_id`. pub fn notifyUpdate(ws: *WebServer) void { - const io = ws.maker.graph.io; + const io = ws.graph.io; _ = ws.update_id.rmw(.Add, 1, .release); io.futexWake(u32, &ws.update_id.raw, 16); } pub const Options = struct { - maker: *Maker, + graph: *const Graph, root_prog_node: std.Progress.Node, listen_address: net.IpAddress, base_timestamp: Io.Clock.Timestamp, }; + pub fn init(opts: Options) WebServer { // The upcoming `Io` interface should allow us to use `Io.async` and `Io.concurrent` // instead of threads, so that the web server can function in single-threaded builds. comptime assert(!builtin.single_threaded); assert(opts.base_timestamp.clock == base_clock); + return .{ + .graph = opts.graph, + .listen_address = opts.listen_address, + .root_prog_node = opts.root_prog_node, - const maker = opts.maker; + .tcp_server = null, + .serve_task = null, + + .base_timestamp = opts.base_timestamp.raw, + + .fuzz = null, + + .build_status = .init(.idle), + .update_id = .init(0), + + .runner_request_mutex = .init, + .runner_request_ready_cond = .init, + .runner_request_empty_cond = .init, + .runner_request = null, + + .configured = null, + }; +} + +pub fn deinit(ws: *WebServer) void { + const graph = ws.graph; + const io = graph.io; + + if (ws.fuzz) |*f| f.deinit(); + + ws.releaseConfigured(); + + if (ws.serve_task) |t| { + if (ws.tcp_server) |*s| s.stream.close(io); + t.await(); + } + if (ws.tcp_server) |*s| s.deinit(); +} + +fn releaseConfigured(ws: *WebServer) void { + if (ws.configured) |*configured| { + const gpa = configured.maker.gpa; + gpa.free(configured.step_names_trailing); + gpa.free(configured.step_status_bits); + for (configured.time_report_msgs) |msg| gpa.free(msg); + gpa.free(configured.time_report_msgs); + gpa.free(configured.time_report_update_times); + gpa.free(configured.step_names_trailing); + ws.configured = null; + } +} + +pub fn updateConfiguration(ws: *WebServer, maker: *Maker) !void { + const graph = ws.graph; + const gpa = maker.gpa; const all_steps = maker.step_stack.keys(); const c = &maker.scanned_config.configuration; - const gpa = maker.gpa; - const graph = maker.graph; - const step_names_trailing = gpa.alloc(u8, len: { + const step_names_trailing = try gpa.alloc(u8, len: { var name_bytes: usize = 0; for (all_steps) |step_index| name_bytes += step_index.ptr(c).name.slice(c).len; break :len name_bytes + all_steps.len * 4; - }) catch @panic("out of memory"); + }); + errdefer gpa.free(step_names_trailing); + { const step_name_lens: []align(1) u32 = @ptrCast(step_names_trailing[0 .. all_steps.len * 4]); var idx: usize = all_steps.len * 4; @@ -103,71 +163,35 @@ pub fn init(opts: Options) WebServer { assert(idx == step_names_trailing.len); } - const step_status_bits = gpa.alloc( - u8, - std.math.divCeil(usize, all_steps.len, 4) catch unreachable, - ) catch @panic("out of memory"); + const step_status_bits = try gpa.alloc(u8, std.math.divCeil(usize, all_steps.len, 4) catch unreachable); + errdefer gpa.free(step_status_bits); @memset(step_status_bits, 0); const time_reports_len: usize = if (graph.time_report) all_steps.len else 0; - const time_report_msgs = gpa.alloc([]u8, time_reports_len) catch @panic("out of memory"); - const time_report_update_times = gpa.alloc(i64, time_reports_len) catch @panic("out of memory"); + const time_report_msgs = try gpa.alloc([]u8, time_reports_len); + errdefer gpa.free(time_report_msgs); + const time_report_update_times = try gpa.alloc(i64, time_reports_len); + errdefer gpa.free(time_report_update_times); @memset(time_report_msgs, &.{}); @memset(time_report_update_times, std.math.minInt(i64)); - return .{ + ws.releaseConfigured(); + + ws.configured = .{ .maker = maker, - .listen_address = opts.listen_address, - .root_prog_node = opts.root_prog_node, - - .tcp_server = null, - .serve_task = null, - - .base_timestamp = opts.base_timestamp.raw, .step_names_trailing = step_names_trailing, - .step_status_bits = step_status_bits, - - .fuzz = null, .time_report_mutex = .init, .time_report_msgs = time_report_msgs, .time_report_update_times = time_report_update_times, - - .build_status = .init(.idle), - .update_id = .init(0), - - .runner_request_mutex = .init, - .runner_request_ready_cond = .init, - .runner_request_empty_cond = .init, - .runner_request = null, }; } -pub fn deinit(ws: *WebServer) void { - const maker = ws.maker; - const gpa = maker.gpa; - const io = maker.graph.io; - gpa.free(ws.step_names_trailing); - gpa.free(ws.step_status_bits); - - if (ws.fuzz) |*f| f.deinit(); - for (ws.time_report_msgs) |msg| gpa.free(msg); - gpa.free(ws.time_report_msgs); - gpa.free(ws.time_report_update_times); - - if (ws.serve_task) |t| { - if (ws.tcp_server) |*s| s.stream.close(io); - t.await(); - } - if (ws.tcp_server) |*s| s.deinit(); - - gpa.free(ws.step_names_trailing); -} pub fn start(ws: *WebServer) error{AlreadyReported}!void { assert(ws.tcp_server == null); assert(ws.serve_task == null); - const maker = ws.maker; - const io = maker.graph.io; + const graph = ws.graph; + const io = graph.io; ws.tcp_server = ws.listen_address.listen(io, .{ .reuse_address = true }) catch |err| { log.err("failed to listen to port {d}: {t}", .{ ws.listen_address.getPort(), err }); @@ -186,8 +210,8 @@ pub fn start(ws: *WebServer) error{AlreadyReported}!void { } } fn serve(ws: *WebServer) Io.Cancelable!void { - const maker = ws.maker; - const io = maker.graph.io; + const graph = ws.graph; + const io = graph.io; var group: Io.Group = .init; defer group.cancel(io); @@ -213,7 +237,8 @@ pub fn startBuild(ws: *WebServer) void { fuzz.deinit(); ws.fuzz = null; } - for (ws.step_status_bits) |*bits| @atomicStore(u8, bits, 0, .monotonic); + const configured = &ws.configured.?; + for (configured.step_status_bits) |*bits| @atomicStore(u8, bits, 0, .monotonic); ws.build_status.store(.running, .monotonic); ws.notifyUpdate(); } @@ -223,12 +248,13 @@ pub fn updateStepStatus( step_index: Configuration.Step.Index, new_status: abi.StepUpdate.Status, ) void { - const maker = ws.maker; + const configured = &ws.configured.?; + const maker = configured.maker; const all_steps = maker.step_stack.keys(); const step_idx: u32 = for (all_steps, 0..) |s, i| { if (s == step_index) break @intCast(i); } else unreachable; - const ptr = &ws.step_status_bits[step_idx / 4]; + const ptr = &configured.step_status_bits[step_idx / 4]; const bit_offset: u3 = @intCast((step_idx % 4) * 2); const old_bits: u2 = @truncate(@atomicLoad(u8, ptr, .monotonic) >> bit_offset); const mask = @as(u8, @intFromEnum(new_status) ^ old_bits) << bit_offset; @@ -239,7 +265,8 @@ pub fn updateStepStatus( pub fn finishBuild(ws: *WebServer, opts: struct { fuzz: bool, }) void { - const maker = ws.maker; + const configured = &ws.configured.?; + const maker = configured.maker; const all_steps = maker.step_stack.keys(); if (opts.fuzz) { @@ -274,15 +301,15 @@ pub fn finishBuild(ws: *WebServer, opts: struct { } pub fn now(ws: *const WebServer) i64 { - const maker = ws.maker; - const io = maker.graph.io; + const graph = ws.graph; + const io = graph.io; const ts = base_clock.now(io); return @intCast(ws.base_timestamp.durationTo(ts).toNanoseconds()); } fn accept(ws: *WebServer, stream: net.Stream) void { - const maker = ws.maker; - const io = maker.graph.io; + const graph = ws.graph; + const io = graph.io; defer { // `net.Stream.close` wants to helpfully overwrite `stream` with @@ -328,17 +355,19 @@ fn accept(ws: *WebServer, stream: net.Stream) void { } fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { - const maker = ws.maker; - const gpa = maker.gpa; - const graph = maker.graph; + const graph = ws.graph; + const gpa = graph.cache.gpa; const io = graph.io; + log.err("TODO serve a different message when the configuration changes", .{}); + const configured = &ws.configured.?; + const maker = configured.maker; const all_steps = maker.step_stack.keys(); var prev_build_status = ws.build_status.load(.monotonic); - const prev_step_status_bits = try gpa.alloc(u8, ws.step_status_bits.len); + const prev_step_status_bits = try gpa.alloc(u8, configured.step_status_bits.len); defer gpa.free(prev_step_status_bits); - for (prev_step_status_bits, ws.step_status_bits) |*copy, *shared| { + for (prev_step_status_bits, configured.step_status_bits) |*copy, *shared| { copy.* = @atomicLoad(u8, shared, .monotonic); } @@ -354,7 +383,7 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { .timestamp = ws.now(), .steps_len = @intCast(all_steps.len), }; - var bufs: [3][]const u8 = .{ @ptrCast(&hello_header), ws.step_names_trailing, prev_step_status_bits }; + var bufs: [3][]const u8 = .{ @ptrCast(&hello_header), configured.step_names_trailing, prev_step_status_bits }; try sock.writeMessageVec(&bufs, .binary); } @@ -369,17 +398,17 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { } { - try ws.time_report_mutex.lock(io); - defer ws.time_report_mutex.unlock(io); - for (ws.time_report_msgs, ws.time_report_update_times) |msg, update_time| { + try configured.time_report_mutex.lock(io); + defer configured.time_report_mutex.unlock(io); + for (configured.time_report_msgs, configured.time_report_update_times) |msg, update_time| { if (update_time <= prev_time) continue; - // We want to send `msg`, but shouldn't block `ws.time_report_mutex` while we do, so + // We want to send `msg`, but shouldn't block `configured.time_report_mutex` while we do, so // that we don't hold up the build system on the client accepting this packet. const owned_msg = try gpa.dupe(u8, msg); defer gpa.free(owned_msg); // Temporarily unlock, then re-lock after the message is sent. - ws.time_report_mutex.unlock(io); - defer ws.time_report_mutex.lockUncancelable(io); + configured.time_report_mutex.unlock(io); + defer configured.time_report_mutex.lockUncancelable(io); try sock.writeMessage(owned_msg, .binary); } } @@ -393,7 +422,7 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { } } - for (prev_step_status_bits, ws.step_status_bits, 0..) |*prev_byte, *shared, byte_idx| { + for (prev_step_status_bits, configured.step_status_bits, 0..) |*prev_byte, *shared, byte_idx| { const cur_byte = @atomicLoad(u8, shared, .monotonic); if (prev_byte.* == cur_byte) continue; const cur: [4]abi.StepUpdate.Status = .{ @@ -433,8 +462,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { } } fn recvWebSocketMessages(ws: *WebServer, sock: *http.Server.WebSocket) void { - const maker = ws.maker; - const io = maker.graph.io; + const graph = ws.graph; + const io = graph.io; while (true) { const msg = sock.readSmallMessage() catch return; @@ -492,8 +521,7 @@ fn serveLibFile( sub_path: []const u8, content_type: []const u8, ) !void { - const maker = ws.maker; - const graph = maker.graph; + const graph = ws.graph; return serveFile(ws, request, .{ .root_dir = graph.zig_lib_directory, @@ -505,7 +533,7 @@ fn serveClientWasm( req: *http.Server.Request, optimize_mode: std.builtin.OptimizeMode, ) !void { - const gpa = ws.maker.gpa; + const gpa = ws.graph.cache.gpa; var arena_state: std.heap.ArenaAllocator = .init(gpa); defer arena_state.deinit(); @@ -522,9 +550,9 @@ pub fn serveFile( path: Cache.Path, content_type: []const u8, ) !void { - const maker = ws.maker; - const gpa = ws.maker.gpa; - const io = maker.graph.io; + const graph = ws.graph; + const gpa = graph.cache.gpa; + const io = graph.io; // The desired API is actually sendfile, which will require enhancing http.Server. // We load the file with every request so that the user can make changes to the file @@ -542,8 +570,7 @@ pub fn serveFile( }); } pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []const Cache.Path) !void { - const maker = ws.maker; - const graph = maker.graph; + const graph = ws.graph; const io = graph.io; var send_buffer: [0x4000]u8 = undefined; @@ -581,9 +608,8 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim const arch_os_abi = "wasm32-freestanding"; const cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext"; - const maker = ws.maker; - const graph = maker.graph; - const gpa = maker.gpa; + const graph = ws.graph; + const gpa = graph.cache.gpa; const io = graph.io; const main_src_path: Cache.Path = .{ @@ -651,9 +677,11 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct { /// The trailing data of `abi.time_report.CompileResult`, except the step name. trailing: []const u8, }) void { - const maker = ws.maker; + const graph = ws.graph; + const io = graph.io; + const configured = &ws.configured.?; + const maker = configured.maker; const gpa = maker.gpa; - const io = maker.graph.io; const all_steps = maker.step_stack.keys(); const step_idx: u32 = for (all_steps, 0..) |s, i| { @@ -661,10 +689,10 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct { } else unreachable; const old_buf = old: { - ws.time_report_mutex.lock(io) catch return; - defer ws.time_report_mutex.unlock(io); - const old = ws.time_report_msgs[step_idx]; - ws.time_report_msgs[step_idx] = &.{}; + configured.time_report_mutex.lock(io) catch return; + defer configured.time_report_mutex.unlock(io); + const old = configured.time_report_msgs[step_idx]; + configured.time_report_msgs[step_idx] = &.{}; break :old old; }; const buf = gpa.realloc(old_buf, @sizeOf(abi.time_report.CompileResult) + opts.trailing.len) catch @panic("out of memory"); @@ -684,19 +712,21 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct { @memcpy(buf[@sizeOf(abi.time_report.CompileResult)..], opts.trailing); { - ws.time_report_mutex.lock(io) catch return; - defer ws.time_report_mutex.unlock(io); - assert(ws.time_report_msgs[step_idx].len == 0); - ws.time_report_msgs[step_idx] = buf; - ws.time_report_update_times[step_idx] = ws.now(); + configured.time_report_mutex.lock(io) catch return; + defer configured.time_report_mutex.unlock(io); + assert(configured.time_report_msgs[step_idx].len == 0); + configured.time_report_msgs[step_idx] = buf; + configured.time_report_update_times[step_idx] = ws.now(); } ws.notifyUpdate(); } pub fn updateTimeReportGeneric(ws: *WebServer, step_index: Configuration.Step.Index, duration: Io.Duration) void { - const maker = ws.maker; + const graph = ws.graph; + const io = graph.io; + const configured = &ws.configured.?; + const maker = configured.maker; const gpa = maker.gpa; - const io = maker.graph.io; const all_steps = maker.step_stack.keys(); const step_idx: u32 = for (all_steps, 0..) |s, i| { @@ -704,10 +734,10 @@ pub fn updateTimeReportGeneric(ws: *WebServer, step_index: Configuration.Step.In } else unreachable; const old_buf = old: { - ws.time_report_mutex.lock(io) catch return; - defer ws.time_report_mutex.unlock(io); - const old = ws.time_report_msgs[step_idx]; - ws.time_report_msgs[step_idx] = &.{}; + configured.time_report_mutex.lock(io) catch return; + defer configured.time_report_mutex.unlock(io); + const old = configured.time_report_msgs[step_idx]; + configured.time_report_msgs[step_idx] = &.{}; break :old old; }; const buf = gpa.realloc(old_buf, @sizeOf(abi.time_report.GenericResult)) catch @panic("out of memory"); @@ -717,11 +747,11 @@ pub fn updateTimeReportGeneric(ws: *WebServer, step_index: Configuration.Step.In .ns_total = @intCast(duration.toNanoseconds()), }; { - ws.time_report_mutex.lock(io) catch return; - defer ws.time_report_mutex.unlock(io); - assert(ws.time_report_msgs[step_idx].len == 0); - ws.time_report_msgs[step_idx] = buf; - ws.time_report_update_times[step_idx] = ws.now(); + configured.time_report_mutex.lock(io) catch return; + defer configured.time_report_mutex.unlock(io); + assert(configured.time_report_msgs[step_idx].len == 0); + configured.time_report_msgs[step_idx] = buf; + configured.time_report_update_times[step_idx] = ws.now(); } ws.notifyUpdate(); } @@ -732,9 +762,11 @@ pub fn updateTimeReportRunTest( tests: *const Step.Run.CachedTestMetadata, ns_per_test: []const u64, ) void { - const maker = ws.maker; + const graph = ws.graph; + const io = graph.io; + const configured = &ws.configured.?; + const maker = configured.maker; const gpa = maker.gpa; - const io = maker.graph.io; const all_steps = maker.step_stack.keys(); const step_idx: u32 = for (all_steps, 0..) |s, i| { @@ -752,10 +784,10 @@ pub fn updateTimeReportRunTest( break :len @sizeOf(abi.time_report.RunTestResult) + names_len + 8 * tests_len; }; const old_buf = old: { - ws.time_report_mutex.lock(io) catch return; - defer ws.time_report_mutex.unlock(io); - const old = ws.time_report_msgs[step_idx]; - ws.time_report_msgs[step_idx] = &.{}; + configured.time_report_mutex.lock(io) catch return; + defer configured.time_report_mutex.unlock(io); + const old = configured.time_report_msgs[step_idx]; + configured.time_report_msgs[step_idx] = &.{}; break :old old; }; const buf = gpa.realloc(old_buf, new_len) catch @panic("out of memory"); @@ -778,11 +810,11 @@ pub fn updateTimeReportRunTest( assert(offset == buf.len); { - ws.time_report_mutex.lock(io) catch return; - defer ws.time_report_mutex.unlock(io); - assert(ws.time_report_msgs[step_idx].len == 0); - ws.time_report_msgs[step_idx] = buf; - ws.time_report_update_times[step_idx] = ws.now(); + configured.time_report_mutex.lock(io) catch return; + defer configured.time_report_mutex.unlock(io); + assert(configured.time_report_msgs[step_idx].len == 0); + configured.time_report_msgs[step_idx] = buf; + configured.time_report_update_times[step_idx] = ws.now(); } ws.notifyUpdate(); } @@ -791,7 +823,7 @@ const RunnerRequest = union(enum) { rebuild, }; pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest { - const io = ws.maker.graph.io; + const io = ws.graph.io; ws.runner_request_mutex.lock(io) catch return; defer ws.runner_request_mutex.unlock(io); if (ws.runner_request) |req| { @@ -802,7 +834,7 @@ pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest { return null; } pub fn wait(ws: *WebServer) Io.Cancelable!RunnerRequest { - const io = ws.maker.graph.io; + const io = ws.graph.io; try ws.runner_request_mutex.lock(io); defer ws.runner_request_mutex.unlock(io); while (true) { -- 2.54.0 From 56db3dd85f794803f462cffc38225a67a27401cc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 20:07:42 -0700 Subject: [PATCH 33/49] Maker: convert some process exits to error codes --- lib/compiler/Maker.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 436ff43895cd049af0cf528fb789575cc83a86d2..eac82740a9f95032b813acbaad92e8ba0bee6b46 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -908,7 +908,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { try group.await(io); for (options.forks) |*fork| { - if (fork.failed) process.exit(1); + if (fork.failed) return error.AlreadyReported; try fork_set.put(arena, .{ .path = fork.path, .manifest_ast = fork.manifest_ast, @@ -1086,7 +1086,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { }); } } - if (any_unused) process.exit(1); + if (any_unused) return error.FailedButCacheIntact; } try job_queue.consolidateErrors(); @@ -1293,7 +1293,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { } try unlazy_set.put(arena, .fromSlice(hash), {}); } - if (any_errors) process.exit(1); + if (any_errors) return error.FailedButCacheIntact; if (options.system_pkg_dir_path) |p| { // In this mode, the system needs to provide these packages; they // cannot be fetched by Zig. @@ -1303,7 +1303,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { } log.info("remote package fetching disabled due to --system mode", .{}); log.info("dependencies might be avoidable depending on build configuration", .{}); - process.exit(1); + return error.FailedButCacheIntact; } continue :cp; } -- 2.54.0 From 39342e6ce73467474388ad53fc1f897862b53064 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 20:16:41 -0700 Subject: [PATCH 34/49] Maker: set web_server field earlier --- lib/compiler/Maker.zig | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index eac82740a9f95032b813acbaad92e8ba0bee6b46..cb252f1509713ecf1ae6fc88f8f7783cf3bd4d24 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -724,7 +724,7 @@ pub fn main(init: process.Init.Minimal) !void { .unit_test_timeout_ns = test_timeout_ns, .watch = watch, - .web_server = undefined, // set after `prepare` + .web_server = web_server, .memory_blocked_steps = .empty, .step_stack = .empty, .pkg_config = .{ .debug = debug_pkg_config }, @@ -745,6 +745,8 @@ pub fn main(init: process.Init.Minimal) !void { maker.prepare(step_names.items) catch |err| switch (err) { error.DependencyLoopDetected, error.InsufficientMemory => { + // TODO handle DependencyLoopDetected as error.FailedButCacheIntact + // and handle InsufficientMemory as error.AlreadyReported _ = io.lockStderr(&.{}, graph.stderr_mode) catch {}; process.exit(1); }, @@ -838,7 +840,10 @@ pub fn main(init: process.Init.Minimal) !void { break :w false; }, }; - if (!server_mode) process.exit(1); + if (!server_mode) { + _ = io.lockStderr(&.{}, graph.stderr_mode) catch {}; + process.exit(1); + } if (can_fs_watch) { @panic("TODO set up fs watching"); } else { -- 2.54.0 From 10bd6cad759fa37d2b6c127ad97635f83261c4ac Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2026 21:27:04 -0700 Subject: [PATCH 35/49] implement zig libc inside maker process The jitcmd mechanism is handy but let's not go overboard. There is overhead when users have to wait for each subcommand independently. We can save time by combining some stuff together. --- lib/compiler/Maker.zig | 128 ++++++++++++++++++++++++++++- lib/compiler/libc.zig | 140 -------------------------------- lib/compiler/resinator/main.zig | 4 +- lib/std/zig/LibCDirs.zig | 47 ++++------- src/Compilation.zig | 2 +- src/main.zig | 9 +- 6 files changed, 147 insertions(+), 183 deletions(-) delete mode 100644 lib/compiler/libc.zig diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index cb252f1509713ecf1ae6fc88f8f7783cf3bd4d24..8e5bcf2474983422f72b753a866d638439503979 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -187,9 +187,10 @@ pub fn main(init: process.Init.Minimal) !void { .random_seed = parseRandomSeed(seed_arg), }; - const cmd = stringToEnum(enum { init, fetch, build }, cmd_name) orelse + 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 => {}, @@ -1746,6 +1747,25 @@ const usage_init = \\ ; +const usage_libc = + \\Usage: zig libc + \\ + \\ Detect the native libc installation and print the resulting + \\ paths to stdout. You can save this into a file and then edit + \\ the paths to create a cross compilation libc kit. Then you + \\ can pass `--libc [file]` for Zig to use it. + \\ + \\Usage: zig libc [paths_file] + \\ + \\ Parse a libc installation text file and validate it. + \\ + \\Options: + \\ -h, --help Print this help and exit + \\ -target [name] -- see the targets command + \\ -includes Print the libc include directories for the target + \\ +; + fn cmdInit(gpa: Allocator, graph: *Graph, args: []const []const u8) !void { const arena = graph.arena; const io = graph.io; @@ -1851,6 +1871,112 @@ fn cmdInit(gpa: Allocator, graph: *Graph, args: []const []const u8) !void { } } +fn cmdLibC(gpa: Allocator, graph: *Graph, args: []const []const u8) !void { + const environ_map = &graph.environ_map; + const io = graph.io; + const arena = graph.arena; + const LibCInstallation = std.zig.LibCInstallation; + + var input_file: ?[]const u8 = null; + var target_arch_os_abi: []const u8 = "native"; + var print_includes: bool = false; + const stdout = initStdoutWriter(io); + { + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { + try stdout.writeAll(usage_libc); + try stdout.flush(); + return std.process.cleanExit(io); + } else if (mem.eql(u8, arg, "-target")) { + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); + i += 1; + target_arch_os_abi = args[i]; + } else if (mem.eql(u8, arg, "-includes")) { + print_includes = true; + } else { + fatal("unrecognized parameter: '{s}'", .{arg}); + } + } else if (input_file != null) { + fatal("unexpected extra parameter: '{s}'", .{arg}); + } else { + input_file = arg; + } + } + } + + const target_query = std.zig.parseTargetQueryOrReportFatalError(gpa, .{ + .arch_os_abi = target_arch_os_abi, + }); + const target = std.zig.resolveTargetQueryOrFatal(io, target_query); + + if (print_includes) { + const libc_installation: ?*LibCInstallation = libc: { + if (input_file) |libc_file| { + const libc = try arena.create(LibCInstallation); + libc.* = LibCInstallation.parse(arena, io, libc_file, &target) catch |err| { + fatal("unable to parse libc file at path {s}: {t}", .{ libc_file, err }); + }; + break :libc libc; + } else { + break :libc null; + } + }; + + const is_native_abi = target_query.isNativeAbi(); + + const libc_dirs = std.zig.LibCDirs.detect( + arena, + io, + .{ .root_dir = graph.zig_lib_directory }, + &target, + is_native_abi, + true, + libc_installation, + environ_map, + ) catch |err| { + const zig_target = try target.zigTriple(arena); + fatal("unable to detect libc for target {s}: {t}", .{ zig_target, err }); + }; + + if (libc_dirs.libc_include_dir_list.len == 0) { + const zig_target = try target.zigTriple(arena); + fatal("no include dirs detected for target {s}", .{zig_target}); + } + + for (libc_dirs.libc_include_dir_list) |include_dir| { + try stdout.writeAll(include_dir); + try stdout.writeByte('\n'); + } + try stdout.flush(); + return std.process.cleanExit(io); + } + + if (input_file) |libc_file| { + var libc = LibCInstallation.parse(gpa, io, libc_file, &target) catch |err| { + fatal("unable to parse libc file at path {s}: {t}", .{ libc_file, err }); + }; + defer libc.deinit(gpa); + } else { + if (!target_query.canDetectLibC()) { + fatal("unable to detect libc for non-native target", .{}); + } + var libc = LibCInstallation.findNative(gpa, io, .{ + .verbose = true, + .target = &target, + .environ_map = environ_map, + }) catch |err| { + fatal("unable to detect native libc: {t}", .{err}); + }; + defer libc.deinit(gpa); + + try libc.render(stdout); + try stdout.flush(); + } +} + fn markFailedStepsDirty(maker: *Maker) void { const all_steps = maker.step_stack.keys(); diff --git a/lib/compiler/libc.zig b/lib/compiler/libc.zig deleted file mode 100644 index 8ca53fefc905c01527c7157bee4c0d10575e582e..0000000000000000000000000000000000000000 --- a/lib/compiler/libc.zig +++ /dev/null @@ -1,140 +0,0 @@ -const std = @import("std"); -const Io = std.Io; -const mem = std.mem; -const LibCInstallation = std.zig.LibCInstallation; - -const usage_libc = - \\Usage: zig libc - \\ - \\ Detect the native libc installation and print the resulting - \\ paths to stdout. You can save this into a file and then edit - \\ the paths to create a cross compilation libc kit. Then you - \\ can pass `--libc [file]` for Zig to use it. - \\ - \\Usage: zig libc [paths_file] - \\ - \\ Parse a libc installation text file and validate it. - \\ - \\Options: - \\ -h, --help Print this help and exit - \\ -target [name] -- see the targets command - \\ -includes Print the libc include directories for the target - \\ -; - -var stdout_buffer: [4096]u8 = undefined; - -pub fn main(init: std.process.Init) !void { - const arena = init.arena.allocator(); - const gpa = init.gpa; - const io = init.io; - const args = try init.minimal.args.toSlice(arena); - const environ_map = init.environ_map; - - const zig_lib_directory = args[1]; - - var input_file: ?[]const u8 = null; - var target_arch_os_abi: []const u8 = "native"; - var print_includes: bool = false; - var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer); - const stdout = &stdout_writer.interface; - { - var i: usize = 2; - while (i < args.len) : (i += 1) { - const arg = args[i]; - if (mem.startsWith(u8, arg, "-")) { - if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - try stdout.writeAll(usage_libc); - try stdout.flush(); - return std.process.cleanExit(io); - } else if (mem.eql(u8, arg, "-target")) { - if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); - i += 1; - target_arch_os_abi = args[i]; - } else if (mem.eql(u8, arg, "-includes")) { - print_includes = true; - } else { - fatal("unrecognized parameter: '{s}'", .{arg}); - } - } else if (input_file != null) { - fatal("unexpected extra parameter: '{s}'", .{arg}); - } else { - input_file = arg; - } - } - } - - const target_query = std.zig.parseTargetQueryOrReportFatalError(gpa, .{ - .arch_os_abi = target_arch_os_abi, - }); - const target = std.zig.resolveTargetQueryOrFatal(io, target_query); - - if (print_includes) { - const libc_installation: ?*LibCInstallation = libc: { - if (input_file) |libc_file| { - const libc = try arena.create(LibCInstallation); - libc.* = LibCInstallation.parse(arena, io, libc_file, &target) catch |err| { - fatal("unable to parse libc file at path {s}: {t}", .{ libc_file, err }); - }; - break :libc libc; - } else { - break :libc null; - } - }; - - const is_native_abi = target_query.isNativeAbi(); - - const libc_dirs = std.zig.LibCDirs.detect( - arena, - io, - zig_lib_directory, - &target, - is_native_abi, - true, - libc_installation, - environ_map, - ) catch |err| { - const zig_target = try target.zigTriple(arena); - fatal("unable to detect libc for target {s}: {t}", .{ zig_target, err }); - }; - - if (libc_dirs.libc_include_dir_list.len == 0) { - const zig_target = try target.zigTriple(arena); - fatal("no include dirs detected for target {s}", .{zig_target}); - } - - for (libc_dirs.libc_include_dir_list) |include_dir| { - try stdout.writeAll(include_dir); - try stdout.writeByte('\n'); - } - try stdout.flush(); - return std.process.cleanExit(io); - } - - if (input_file) |libc_file| { - var libc = LibCInstallation.parse(gpa, io, libc_file, &target) catch |err| { - fatal("unable to parse libc file at path {s}: {t}", .{ libc_file, err }); - }; - defer libc.deinit(gpa); - } else { - if (!target_query.canDetectLibC()) { - fatal("unable to detect libc for non-native target", .{}); - } - var libc = LibCInstallation.findNative(gpa, io, .{ - .verbose = true, - .target = &target, - .environ_map = environ_map, - }) catch |err| { - fatal("unable to detect native libc: {t}", .{err}); - }; - defer libc.deinit(gpa); - - try libc.render(stdout); - try stdout.flush(); - } -} - -fn fatal(comptime format: []const u8, args: anytype) noreturn { - std.log.err(format, args); - std.process.exit(1); -} diff --git a/lib/compiler/resinator/main.zig b/lib/compiler/resinator/main.zig index e962177015641502fdce3bcd6d9b6adf1195d0e3..170a6c02d3606a93c43c85901157da4c967bb0d3 100644 --- a/lib/compiler/resinator/main.zig +++ b/lib/compiler/resinator/main.zig @@ -639,7 +639,7 @@ fn getIncludePaths( }; const target = std.zig.resolveTargetQueryOrFatal(io, target_query); const is_native_abi = target_query.isNativeAbi(); - const detected_libc = std.zig.LibCDirs.detect(arena, io, zig_lib_dir, &target, is_native_abi, true, null, environ_map) catch { + const detected_libc = std.zig.LibCDirs.detect(arena, io, .{ .root_dir = .cwd, .sub_path = zig_lib_dir }, &target, is_native_abi, true, null, environ_map) catch { if (includes == .any) { // fall back to mingw includes = .gnu; @@ -668,7 +668,7 @@ fn getIncludePaths( const detected_libc = std.zig.LibCDirs.detect( arena, io, - zig_lib_dir, + .{ .root_dir = .cwd, .sub_path = zig_lib_dir }, &target, is_native_abi, true, diff --git a/lib/std/zig/LibCDirs.zig b/lib/std/zig/LibCDirs.zig index 04c61af0bedab6728309db7c00c975ef4229a57d..84002241bf9ea4fb1dbf5f8702a4723c05a01694 100644 --- a/lib/std/zig/LibCDirs.zig +++ b/lib/std/zig/LibCDirs.zig @@ -5,6 +5,7 @@ const std = @import("../std.zig"); const Io = std.Io; const LibCInstallation = std.zig.LibCInstallation; const Allocator = std.mem.Allocator; +const Path = std.Build.Cache.Path; libc_include_dir_list: []const []const u8, libc_installation: ?*const LibCInstallation, @@ -23,7 +24,7 @@ pub const DarwinSdkLayout = enum { pub fn detect( arena: Allocator, io: Io, - zig_lib_dir: []const u8, + zig_lib_dir: Path, target: *const std.Target, is_native_abi: bool, link_libc: bool, @@ -166,20 +167,12 @@ fn detectFromInstallation(arena: Allocator, target: *const std.Target, lci: *con }; } -pub fn detectFromBuilding( - arena: Allocator, - zig_lib_dir: []const u8, - target: *const std.Target, -) !LibCDirs { +pub fn detectFromBuilding(arena: Allocator, zig_lib_dir: Path, target: *const std.Target) !LibCDirs { const s = std.fs.path.sep_str; if (target.os.tag.isDarwin()) { const list = try arena.alloc([]const u8, 1); - list[0] = try std.fmt.allocPrint( - arena, - "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-darwin-any", - .{zig_lib_dir}, - ); + list[0] = try arena.print("{f}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-darwin-any", .{zig_lib_dir}); return .{ .libc_include_dir_list = list, .libc_installation = null, @@ -212,27 +205,19 @@ pub fn detectFromBuilding( std.zig.target.netbsdAbiNameHeaders(target.abi) else @tagName(target.abi); - const arch_include_dir = try std.fmt.allocPrint( - arena, - "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}", - .{ zig_lib_dir, arch_name, os_name, abi_name }, - ); - const generic_include_dir = try std.fmt.allocPrint( - arena, - "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}", - .{ zig_lib_dir, generic_name }, - ); + const arch_include_dir = try arena.print("{f}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}", .{ + zig_lib_dir, arch_name, os_name, abi_name, + }); + const generic_include_dir = try arena.print("{f}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}", .{ + zig_lib_dir, generic_name, + }); const generic_arch_name = std.zig.target.osArchName(target); - const arch_os_include_dir = try std.fmt.allocPrint( - arena, - "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any", - .{ zig_lib_dir, generic_arch_name, os_name }, - ); - const generic_os_include_dir = try std.fmt.allocPrint( - arena, - "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any", - .{ zig_lib_dir, os_name }, - ); + const arch_os_include_dir = try arena.print("{f}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any", .{ + zig_lib_dir, generic_arch_name, os_name, + }); + const generic_os_include_dir = try arena.print("{f}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any", .{ + zig_lib_dir, os_name, + }); const list = try arena.alloc([]const u8, 4); list[0] = arch_include_dir; diff --git a/src/Compilation.zig b/src/Compilation.zig index 3bdc049c3e3ba307a1430027567d50e39b346e1f..1e9b81d493d62ceea21fb74b3786677b119eb9b4 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1729,7 +1729,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, const libc_dirs = std.zig.LibCDirs.detect( arena, io, - options.dirs.zig_lib.path.?, + .{ .root_dir = options.dirs.zig_lib }, target, options.root_mod.resolved_target.is_native_abi, link_libc, diff --git a/src/main.zig b/src/main.zig index 2213920e32ac4402c401f9b1cdf7b8abe9c75c8b..47bf6d2f49517e235cb86ded97a665d8c5c5192e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -352,7 +352,7 @@ fn mainArgs( dev.check(.ar_command); return process.exit(try llvmArMain(arena, args)); }, - .build, .fetch, .init => { + .build, .fetch, .init, .libc => { return jitCmd(gpa, arena, io, cmd_args, environ_map, .{ .cmd_name = "maker", .root_src_path = "Maker.zig", @@ -409,13 +409,6 @@ fn mainArgs( .root_src_path = "objdump.zig", }); }, - .libc => { - return jitCmd(gpa, arena, io, cmd_args, environ_map, .{ - .cmd_name = "libc", - .root_src_path = "libc.zig", - .prepend_zig_lib_dir_path = true, - }); - }, .std => { return jitCmd(gpa, arena, io, cmd_args, environ_map, .{ .cmd_name = "std", -- 2.54.0 From cd28a740b7ba366ce4f8db1a6d75dcff2bdbd606 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 25 Jun 2026 03:41:14 -0700 Subject: [PATCH 36/49] CI: update --maker-opt=Debug to ZIG_DEBUG_CMD --- ci/x86_64-linux-debug.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/x86_64-linux-debug.sh b/ci/x86_64-linux-debug.sh index fe4450ea8e67316d5d75def9c60fb79bf6edb3c6..91f6291d90b345139f394f4ee0c26bbe80a5ac57 100755 --- a/ci/x86_64-linux-debug.sh +++ b/ci/x86_64-linux-debug.sh @@ -44,6 +44,7 @@ ninja install # Must be done after zig cc is finished. export ZIG_LIB_DIR="$PWD/../lib" +export ZIG_DEBUG_CMD=1 # simultaneously test building self-hosted without LLVM and with 32-bit arm stage3-debug/bin/zig build \ @@ -51,7 +52,6 @@ stage3-debug/bin/zig build \ -Dno-lib stage3-debug/bin/zig build test docs \ - --maker-opt=Debug \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dlldb=$HOME/deps/lldb-zig/Debug-7c1090fd46/bin/lldb \ -fqemu \ -- 2.54.0 From 34d3bcb353ef34b3e87b12d6d3acd09110ea6d51 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 25 Jun 2026 12:42:08 -0700 Subject: [PATCH 37/49] Fetch: quiet the debug log a bit --- lib/compiler/Maker/Fetch.zig | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/compiler/Maker/Fetch.zig b/lib/compiler/Maker/Fetch.zig index d730f724d29d44c3bc733686be80df8d73a90b70..7d89556b9be0733f4709643e8956f02695a519d6 100644 --- a/lib/compiler/Maker/Fetch.zig +++ b/lib/compiler/Maker/Fetch.zig @@ -551,9 +551,6 @@ pub fn run(f: *Fetch) RunError!void { // will already have been resolved to no longer have extra ".." or // "." components. assert(job_queue.local_storage != null); - log.debug("checking pkg root \"{s}\" against parent package root \"{s}\"", .{ - pkg_root.sub_path, f.remote_package_root.sub_path, - }); assert(pkg_root.root_dir.eql(f.remote_package_root.root_dir)); if (!std.mem.startsWith(u8, pkg_root.sub_path, f.remote_package_root.sub_path)) return f.fail( f.location_tok, -- 2.54.0 From 0959167e8161b5b19c1ad836aa35778dc9357c17 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 25 Jun 2026 12:55:41 -0700 Subject: [PATCH 38/49] take advantage of std.mem.Allocator.print --- lib/compiler/Maker.zig | 55 +++++------ lib/compiler/Maker/Step/Compile.zig | 61 ++++++------- lib/compiler/Maker/Step/ObjCopy.zig | 19 ++-- lib/compiler/Maker/Step/Run.zig | 13 ++- lib/compiler/Maker/Step/TranslateC.zig | 7 +- lib/compiler/Maker/Step/UpdateSourceFiles.zig | 1 - lib/compiler/Maker/Step/WriteFile.zig | 1 - lib/std/Build/Step/ConfigHeader.zig | 9 +- lib/std/Build/Step/TranslateC.zig | 3 +- src/link/Lld.zig | 91 +++++++++---------- src/main.zig | 41 ++++----- 11 files changed, 136 insertions(+), 165 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 8e5bcf2474983422f72b753a866d638439503979..8066db7a6224a0e8e1e12443bf35bc1b60561c83 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -21,7 +21,6 @@ const process = std.process; const Color = std.zig.Color; const EnvVar = std.zig.EnvVar; const default_local_zig_cache_basename = std.zig.default_local_zig_cache_basename; -const allocPrint = std.fmt.allocPrint; const stringToEnum = std.meta.stringToEnum; const Fuzz = @import("Maker/Fuzz.zig"); @@ -125,10 +124,10 @@ pub const CliModule = struct { if (mem.eql(u8, name, dep.name)) { argv.appendAssumeCapacity(dep.name); } else { - argv.appendAssumeCapacity(try allocPrint(arena, "{s}={s}", .{ name, dep.name })); + argv.appendAssumeCapacity(try arena.print("{s}={s}", .{ name, dep.name })); } } - argv.appendAssumeCapacity(try allocPrint(arena, "-M{s}={s}", .{ cm.name, cm.root_path })); + argv.appendAssumeCapacity(try arena.print("-M{s}={s}", .{ cm.name, cm.root_path })); } }; @@ -278,7 +277,7 @@ pub fn main(init: process.Init.Minimal) !void { fatalWithHint("expected [auto|on|off] found {q}", .{next_arg}); try cached_passthru_configure.append(arena, @intCast(configure_argv.items.len)); - configure_argv.appendAssumeCapacity(try allocPrint(arena, "--color={t}", .{color})); + 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"); @@ -965,7 +964,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { try build_configurer_argv.appendSlice(gpa, &.{ "--libc", libc_file }); } if (graph.reference_trace) |n| { - try build_configurer_argv.append(gpa, try allocPrint(arena, "-freference-trace={d}", .{n})); + try build_configurer_argv.append(gpa, try arena.print("-freference-trace={d}", .{n})); } if (graph.debug_compile_errors) { try build_configurer_argv.append(gpa, "--debug-compile-errors"); @@ -973,7 +972,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { try build_configurer_argv.appendSlice(gpa, &.{ "--dep", "@build", // "--dep", "@dependencies", // - try allocPrint(arena, "-Mroot={f}", .{configurer_root_src_path}), // + try arena.print("-Mroot={f}", .{configurer_root_src_path}), // }); // In the loop below, after doing the fetch operation, the argv will be @@ -1126,7 +1125,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { const hex_digest = hh.final(); const dependencies_zig_path: Path = .{ .root_dir = graph.local_cache_root, - .sub_path = try allocPrint(arena, "o/{s}/dependencies.zig", .{&hex_digest}), + .sub_path = try arena.print("o/{s}/dependencies.zig", .{&hex_digest}), }; var atomic_file = try dependencies_zig_path.root_dir.handle.createFileAtomic( io, @@ -1195,12 +1194,12 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { if (mem.eql(u8, name, sub.name)) { build_configurer_argv.appendAssumeCapacity(sub.name); } else { - build_configurer_argv.appendAssumeCapacity(try allocPrint(arena, "{s}={s}", .{ + build_configurer_argv.appendAssumeCapacity(try arena.print("{s}={s}", .{ name, sub.name, })); } } - build_configurer_argv.appendAssumeCapacity(try allocPrint(arena, "-M{s}={s}/{s}", .{ + build_configurer_argv.appendAssumeCapacity(try arena.print("-M{s}={s}/{s}", .{ dep.name, dep.root_path, std.zig.build_zig_basename, })); } @@ -1219,7 +1218,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { break :cp .{ .{ .root_dir = graph.local_cache_root, - .sub_path = try allocPrint(arena, "c/{s}", .{&digest}), + .sub_path = try arena.print("c/{s}", .{&digest}), }, false, }; @@ -1326,7 +1325,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { const digest = config_man.final(); const final_path: Path = .{ .root_dir = graph.local_cache_root, - .sub_path = try allocPrint(arena, "c/{s}", .{&digest}), + .sub_path = try arena.print("c/{s}", .{&digest}), }; Io.Dir.rename( config_tmp_path.root_dir.handle, @@ -1597,7 +1596,7 @@ fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void { var saved_path_or_url = path_or_url; if (fetch.latest_commit) |latest_commit| resolved: { - const latest_commit_hex = try allocPrint(arena, "{f}", .{latest_commit}); + const latest_commit_hex = try arena.print("{f}", .{latest_commit}); var uri = try std.Uri.parse(path_or_url); @@ -1610,7 +1609,7 @@ fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void { log.info("resolved ref {q} to commit {s}", .{ target_ref, latest_commit_hex }); // include the original refspec in a query parameter, could be used to check for updates - uri.query = .{ .percent_encoded = try allocPrint(arena, "ref={f}", .{ + uri.query = .{ .percent_encoded = try arena.print("ref={f}", .{ std.fmt.alt(fragment, .formatEscaped), }) }; } else { @@ -1621,12 +1620,12 @@ fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void { uri.fragment = .{ .raw = latest_commit_hex }; switch (save) { - .yes => saved_path_or_url = try allocPrint(arena, "{f}", .{uri}), + .yes => saved_path_or_url = try arena.print("{f}", .{uri}), .no, .exact => {}, // keep the original URL } } - const new_node_init = try allocPrint(arena, + const new_node_init = try arena.print( \\.{{ \\ .url = "{f}", \\ .hash = "{f}", @@ -1636,15 +1635,15 @@ fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void { std.zig.fmtString(package_hash_slice), }); - const new_node_text = try allocPrint(arena, ".{f} = {s},\n", .{ + const new_node_text = try arena.print(".{f} = {s},\n", .{ std.zig.fmtIdPU(name), new_node_init, }); - const dependencies_init = try allocPrint(arena, ".{{\n {s} }}", .{ + const dependencies_init = try arena.print(".{{\n {s} }}", .{ new_node_text, }); - const dependencies_text = try allocPrint(arena, ".dependencies = {s},\n", .{ + const dependencies_text = try arena.print(".dependencies = {s},\n", .{ dependencies_init, }); @@ -1661,16 +1660,8 @@ fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void { } } - const location_replace = try allocPrint( - arena, - "\"{f}\"", - .{std.zig.fmtString(saved_path_or_url)}, - ); - const hash_replace = try allocPrint( - arena, - "\"{f}\"", - .{std.zig.fmtString(package_hash_slice)}, - ); + const location_replace = try arena.print("{q}", .{saved_path_or_url}); + const hash_replace = try arena.print("{q}", .{package_hash_slice}); log.warn("overwriting existing dependency named {q}", .{name}); try fixups.replace_nodes_with_string.put(gpa, dep.location_node, location_replace); @@ -3272,11 +3263,11 @@ pub fn installSymLinks( const name = conf_comp.root_name.slice(c); const filename_major_only, const filename_name_only = if (os_tag.isDarwin()) .{ - try allocPrint(arena, "lib{s}.{d}.dylib", .{ name, version.major }), - try allocPrint(arena, "lib{s}.dylib", .{name}), + try arena.print("lib{s}.{d}.dylib", .{ name, version.major }), + try arena.print("lib{s}.dylib", .{name}), } else .{ - try allocPrint(arena, "lib{s}.so.{d}", .{ name, version.major }), - try allocPrint(arena, "lib{s}.so", .{name}), + try arena.print("lib{s}.so.{d}", .{ name, version.major }), + try arena.print("lib{s}.so", .{name}), }; return installSymLinksInner(maker, arena, output_path, asking_step_index, filename_major_only, filename_name_only); diff --git a/lib/compiler/Maker/Step/Compile.zig b/lib/compiler/Maker/Step/Compile.zig index 33a096027032fddd94349ebc216089fbe9ea564e..675dbc29258a6d8d203d652556cd5dc3eec7be8b 100644 --- a/lib/compiler/Maker/Step/Compile.zig +++ b/lib/compiler/Maker/Step/Compile.zig @@ -10,7 +10,6 @@ const Module = std.Build.Configuration.Module; const Io = std.Io; const Sha256 = std.crypto.hash.sha2.Sha256; const assert = std.debug.assert; -const allocPrint = std.fmt.allocPrint; const Step = @import("../Step.zig"); const Maker = @import("../../Maker.zig"); @@ -179,7 +178,7 @@ fn lowerZigArgs( try zig_args.append(gpa, cmd); if (graph.reference_trace) |some| { - try zig_args.append(gpa, try allocPrint(arena, "-freference-trace={d}", .{some})); + try zig_args.append(gpa, try arena.print("-freference-trace={d}", .{some})); } try addFlag(gpa, zig_args, "allow-so-scripts", conf_comp.flags2.allow_so_scripts.toBool() orelse graph.allow_so_scripts); @@ -191,7 +190,7 @@ fn lowerZigArgs( if (root_module.resolved_target.get(conf).?.query.unwrap()) |query| { if (query.get(conf).flags.object_format.unwrap()) |ofmt| { - try zig_args.append(gpa, try allocPrint(arena, "-ofmt={t}", .{ofmt})); + try zig_args.append(gpa, try arena.print("-ofmt={t}", .{ofmt})); } } @@ -201,7 +200,7 @@ fn lowerZigArgs( .enabled => try zig_args.append(gpa, "-fentry"), .symbol_name => { const symbol_name = conf_comp.entry.value.?.slice(conf); - try zig_args.append(gpa, try allocPrint(arena, "-fentry={s}", .{symbol_name})); + try zig_args.append(gpa, try arena.print("-fentry={s}", .{symbol_name})); }, } @@ -210,7 +209,7 @@ fn lowerZigArgs( } if (conf_comp.stack_size.value) |stack_size| { - try zig_args.appendSlice(gpa, &.{ "--stack", try allocPrint(arena, "{d}", .{stack_size}) }); + try zig_args.appendSlice(gpa, &.{ "--stack", try arena.print("{d}", .{stack_size}) }); } try addBool(gpa, zig_args, "-ffuzz", fuzz); @@ -346,7 +345,7 @@ fn lowerZigArgs( else => |e| return e, } } - try zig_args.append(gpa, try allocPrint(arena, "{s}{s}", .{ + try zig_args.append(gpa, try arena.print("{s}{s}", .{ prefix, system_lib_name, })); } @@ -526,7 +525,7 @@ fn lowerZigArgs( if (mem.eql(u8, import_cli_name, name_slice)) { zig_args.appendAssumeCapacity(import_cli_name); } else { - zig_args.appendAssumeCapacity(try allocPrint(arena, "{s}={s}", .{ + zig_args.appendAssumeCapacity(try arena.print("{s}={s}", .{ name_slice, import_cli_name, })); } @@ -542,9 +541,9 @@ fn lowerZigArgs( try zig_args.ensureUnusedCapacity(gpa, 1); if (mod.root_source_file.unwrap()) |lp| { const src = try maker.resolveLazyPathIndexAbs(arena, lp, compile_index); - zig_args.appendAssumeCapacity(try allocPrint(arena, "-M{s}={s}", .{ module_cli_name, src })); + zig_args.appendAssumeCapacity(try arena.print("-M{s}={s}", .{ module_cli_name, src })); } else if (moduleNeedsCliArg(&mod, conf)) { - zig_args.appendAssumeCapacity(try allocPrint(arena, "-M{s}", .{module_cli_name})); + zig_args.appendAssumeCapacity(try arena.print("-M{s}", .{module_cli_name})); } } } @@ -583,7 +582,7 @@ fn lowerZigArgs( if (conf_comp.image_base.value) |image_base| { (try zig_args.addManyAsArray(gpa, 2)).* = .{ - "--image-base", try allocPrint(arena, "0x{x}", .{image_base}), + "--image-base", try arena.print("0x{x}", .{image_base}), }; } @@ -643,10 +642,10 @@ fn lowerZigArgs( if (!conf_comp.flags.link_z_relro) (try zig_args.addManyAsArray(gpa, 2)).* = .{ "-z", "norelro" }; if (conf_comp.flags.link_z_lazy) (try zig_args.addManyAsArray(gpa, 2)).* = .{ "-z", "lazy" }; if (conf_comp.link_z_common_page_size.value) |size| (try zig_args.addManyAsArray(gpa, 2)).* = .{ - "-z", try allocPrint(arena, "common-page-size={d}", .{size}), + "-z", try arena.print("common-page-size={d}", .{size}), }; if (conf_comp.link_z_max_page_size.value) |size| (try zig_args.addManyAsArray(gpa, 2)).* = .{ - "-z", try allocPrint(arena, "max-page-size={d}", .{size}), + "-z", try arena.print("max-page-size={d}", .{size}), }; if (conf_comp.flags.link_z_defs) (try zig_args.addManyAsArray(gpa, 2)).* = .{ "-z", "defs" }; @@ -667,7 +666,7 @@ fn lowerZigArgs( try zig_args.ensureUnusedCapacity(gpa, 1); if (graph.debug_compiler_runtime_libs) |mode| switch (mode) { .Debug => zig_args.appendAssumeCapacity("--debug-rt"), - else => zig_args.appendAssumeCapacity(try allocPrint(arena, "--debug-rt={t}", .{mode})), + else => zig_args.appendAssumeCapacity(try arena.print("--debug-rt={t}", .{mode})), }; { @@ -691,15 +690,9 @@ fn lowerZigArgs( const abi = root_module_target.flags.abi.unwrap().?; zig_args.addManyAsArrayAssumeCapacity(2).* = .{ "-install_name", - if (conf_comp.install_name.value) |s| s.slice(conf) else try allocPrint( - arena, - "@rpath/{s}{s}{s}", - .{ - os_tag.libPrefix(abi), - conf_comp.root_name.slice(conf), - os_tag.dynamicLibSuffix(), - }, - ), + if (conf_comp.install_name.value) |s| s.slice(conf) else try arena.print("@rpath/{s}{s}{s}", .{ + os_tag.libPrefix(abi), conf_comp.root_name.slice(conf), os_tag.dynamicLibSuffix(), + }), }; } } @@ -712,12 +705,12 @@ fn lowerZigArgs( } if (conf_comp.pagezero_size.value) |pagezero_size| { (try zig_args.addManyAsArray(gpa, 2)).* = .{ - "-pagezero_size", try allocPrint(arena, "{x}", .{pagezero_size}), + "-pagezero_size", try arena.print("{x}", .{pagezero_size}), }; } if (conf_comp.headerpad_size.value) |headerpad_size| { (try zig_args.addManyAsArray(gpa, 2)).* = .{ - "-headerpad", try allocPrint(arena, "{x}", .{headerpad_size}), + "-headerpad", try arena.print("{x}", .{headerpad_size}), }; } try addBool(gpa, zig_args, "-headerpad_max_install_names", conf_comp.flags.headerpad_max_install_names); @@ -740,13 +733,13 @@ fn lowerZigArgs( { try zig_args.ensureUnusedCapacity(gpa, 4); if (conf_comp.initial_memory.value) |initial_memory| { - zig_args.appendAssumeCapacity(try allocPrint(arena, "--initial-memory={d}", .{initial_memory})); + zig_args.appendAssumeCapacity(try arena.print("--initial-memory={d}", .{initial_memory})); } if (conf_comp.max_memory.value) |max_memory| { - zig_args.appendAssumeCapacity(try allocPrint(arena, "--max-memory={d}", .{max_memory})); + zig_args.appendAssumeCapacity(try arena.print("--max-memory={d}", .{max_memory})); } if (conf_comp.global_base.value) |global_base| { - zig_args.appendAssumeCapacity(try allocPrint(arena, "--global-base={d}", .{global_base})); + zig_args.appendAssumeCapacity(try arena.print("--global-base={d}", .{global_base})); } switch (conf_comp.flags3.wasi_exec_model) { .default => {}, @@ -812,15 +805,15 @@ fn lowerZigArgs( if (conf_comp.flags3.build_id.unwrap(conf_comp.build_id.value, conf) orelse graph.build_id) |build_id| { try zig_args.append(gpa, switch (build_id) { - .hexstring => |hs| try allocPrint(arena, "--build-id=0x{x}", .{hs.toSlice()}), - .none, .fast, .uuid, .sha1, .md5 => try allocPrint(arena, "--build-id={t}", .{build_id}), + .hexstring => |hs| try arena.print("--build-id=0x{x}", .{hs.toSlice()}), + .none, .fast, .uuid, .sha1, .md5 => try arena.print("--build-id={t}", .{build_id}), }); } const opt_zig_lib_dir: ?[]const u8 = if (conf_comp.zig_lib_dir.value) |dir| try maker.resolveLazyPathIndexAbs(arena, dir, compile_index) else if (graph.zig_lib_directory.path) |_| - try allocPrint(arena, "{f}", .{graph.zig_lib_directory}) + try arena.print("{f}", .{graph.zig_lib_directory}) else null; @@ -848,7 +841,7 @@ fn lowerZigArgs( try addBool(gpa, zig_args, "-municode", conf_comp.flags.mingw_unicode_entry_point); if (conf_comp.error_limit.value orelse graph.error_limit) |err_limit| (try zig_args.addManyAsArray(gpa, 2)).* = .{ - "--error-limit", try allocPrint(arena, "{d}", .{err_limit}), + "--error-limit", try arena.print("{d}", .{err_limit}), }; try addFlag(gpa, zig_args, "incremental", conf_comp.flags4.incremental.toBool() orelse graph.incremental); @@ -1152,7 +1145,7 @@ const CliNamedModules = struct { try result.modules.putNoClobber(arena, mod, {}); break; } - name = try allocPrint(arena, "{s}{d}", .{ orig_name_slice, n }); + name = try arena.print("{s}{d}", .{ orig_name_slice, n }); n += 1; } } @@ -1307,7 +1300,7 @@ fn appendModuleFlags( } for (m.export_symbol_names.slice) |symbol_name| { - try zig_args.append(gpa, try allocPrint(arena, "--export={s}", .{symbol_name.slice(conf)})); + try zig_args.append(gpa, try arena.print("--export={s}", .{symbol_name.slice(conf)})); } try zig_args.ensureUnusedCapacity(gpa, 2 * m.include_dirs.len); @@ -1375,7 +1368,7 @@ pub fn appendIncludeDirFlags( zig_args.appendAssumeCapacity(try path.toString(arena)); }, .embed_path => |lazy_path| { - zig_args.appendAssumeCapacity(try allocPrint(arena, "--embed-dir={f}", .{ + zig_args.appendAssumeCapacity(try arena.print("--embed-dir={f}", .{ try maker.resolveLazyPathIndex(arena, lazy_path, asking_step), })); }, diff --git a/lib/compiler/Maker/Step/ObjCopy.zig b/lib/compiler/Maker/Step/ObjCopy.zig index 937270a00601575050753421199db0d1964603ff..a6c94e687fd8dfa0c6252aafb95752b9f53489e1 100644 --- a/lib/compiler/Maker/Step/ObjCopy.zig +++ b/lib/compiler/Maker/Step/ObjCopy.zig @@ -3,7 +3,6 @@ const ObjCopy = @This(); const std = @import("std"); const Io = std.Io; const Path = std.Build.Cache.Path; -const allocPrint = std.fmt.allocPrint; const Configuration = std.Build.Configuration; const Step = @import("../Step.zig"); @@ -55,7 +54,7 @@ pub fn make( .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest, basename }), }; if (conf_oc.debug_file.value) |debug_file| { - const debug_basename = opt_debug_basename orelse try allocPrint(arena, "{s}.debug", .{ + const debug_basename = opt_debug_basename orelse try arena.print("{s}.debug", .{ Io.Dir.path.basename(input_path.sub_path), }); maker.generatedPath(debug_file).* = .{ @@ -92,7 +91,7 @@ pub fn make( if (conf_oc.pad_to.value) |pad_to| { argv.addManyAsArrayAssumeCapacity(2).* = .{ - "--pad-to", try allocPrint(arena, "{d}", .{pad_to}), + "--pad-to", try arena.print("{d}", .{pad_to}), }; } @@ -105,14 +104,14 @@ pub fn make( argv.appendAssumeCapacity("--compress-debug-sections"); if (conf_oc.debug_file.value) |debug_file| { - const debug_basename = opt_debug_basename orelse try allocPrint(arena, "{s}.debug", .{ + const debug_basename = opt_debug_basename orelse try arena.print("{s}.debug", .{ Io.Dir.path.basename(input_path.sub_path), }); const debug_dest_path: Path = .{ .root_dir = cache_root, .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest, debug_basename }), }; - argv.appendAssumeCapacity(try allocPrint(arena, "--extract-to={f}", .{debug_dest_path})); + argv.appendAssumeCapacity(try arena.print("--extract-to={f}", .{debug_dest_path})); maker.generatedPath(debug_file).* = debug_dest_path; } @@ -120,7 +119,7 @@ pub fn make( for (conf_oc.add_section.slice) |section| { argv.appendAssumeCapacity("--add-section"); - argv.appendAssumeCapacity(try allocPrint(arena, "{s}={f}", .{ + argv.appendAssumeCapacity(try arena.print("{s}={f}", .{ section.section_name.slice(conf), try maker.resolveLazyPathIndex(arena, section.file_path, step_index), })); @@ -133,14 +132,14 @@ pub fn make( if (update.flags.alignment.toBytes()) |a| { argv.appendAssumeCapacity("--set-section-alignment"); - argv.appendAssumeCapacity(try allocPrint(arena, "{s}={d}", .{ name, a })); + argv.appendAssumeCapacity(try arena.print("{s}={d}", .{ name, a })); } const f = update.flags.section_flags; if (f != Configuration.Step.ObjCopy.SectionFlags.default) { // trailing comma is allowed argv.appendAssumeCapacity("--set-section-flags"); - argv.appendAssumeCapacity(try allocPrint(arena, "{s}={s}{s}{s}{s}{s}{s}{s}{s}{s}", .{ + argv.appendAssumeCapacity(try arena.print("{s}={s}{s}{s}{s}{s}{s}{s}{s}{s}", .{ name, if (f.alloc) "alloc," else "", if (f.contents) "contents," else "", @@ -155,8 +154,8 @@ pub fn make( } } - argv.appendAssumeCapacity(try allocPrint(arena, "{f}", .{input_path})); - argv.appendAssumeCapacity(try allocPrint(arena, "{f}", .{dest_path})); + argv.appendAssumeCapacity(try arena.print("{f}", .{input_path})); + argv.appendAssumeCapacity(try arena.print("{f}", .{dest_path})); argv.appendAssumeCapacity("--listen=-"); _ = Step.evalZigProcess(step_index, maker, argv.items, progress_node, false) catch |err| switch (err) { diff --git a/lib/compiler/Maker/Step/Run.zig b/lib/compiler/Maker/Step/Run.zig index f95ef1ce08979b056acdcfd350ac9aef64c7a958..14da2a4bc22342d9edc5c50666b200f45e274521 100644 --- a/lib/compiler/Maker/Step/Run.zig +++ b/lib/compiler/Maker/Step/Run.zig @@ -12,7 +12,6 @@ const Path = std.Build.Cache.Path; const assert = std.debug.assert; const mem = std.mem; const process = std.process; -const allocPrint = std.fmt.allocPrint; const Allocator = std.mem.Allocator; const Step = @import("../Step.zig"); @@ -196,8 +195,8 @@ pub fn make( const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root }, false); try argv_list.ensureUnusedCapacity(gpa, 3); - argv_list.appendAssumeCapacity(try allocPrint(arena, "--cache-dir={s}", .{cache_dir_string})); - argv_list.appendAssumeCapacity(try allocPrint(arena, "--seed=0x{x}", .{graph.random_seed})); + argv_list.appendAssumeCapacity(try arena.print("--cache-dir={s}", .{cache_dir_string})); + argv_list.appendAssumeCapacity(try arena.print("--seed=0x{x}", .{graph.random_seed})); argv_list.appendAssumeCapacity("--listen=-"); } @@ -1627,8 +1626,8 @@ pub fn rerunInFuzzMode( const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root }, false); try argv_list.ensureUnusedCapacity(gpa, 3); - argv_list.appendAssumeCapacity(try allocPrint(arena, "--cache-dir={s}", .{cache_dir_string})); - argv_list.appendAssumeCapacity(try allocPrint(arena, "--seed=0x{x}", .{graph.random_seed})); + argv_list.appendAssumeCapacity(try arena.print("--cache-dir={s}", .{cache_dir_string})); + argv_list.appendAssumeCapacity(try arena.print("--seed=0x{x}", .{graph.random_seed})); argv_list.appendAssumeCapacity("--listen=-"); } @@ -1925,7 +1924,7 @@ fn runCommand( const path = try maker.resolveLazyPath(arena, lazy_path.get(conf), run_index); path.root_dir.handle.createDirPath(io, path.subPathOrDot()) catch |e| return step.fail(maker, "failed creating directory {f}: {t}", .{ path, e }); - interp_argv.appendAssumeCapacity(try allocPrint(arena, "--dir={f}::{s}", .{ path, name.slice(conf) })); + interp_argv.appendAssumeCapacity(try arena.print("--dir={f}::{s}", .{ path, name.slice(conf) })); } // Wasmtime doeesn't inherit environment variables from the parent process // by default. '-S inherit-env' was added in Wasmtime version 20. @@ -2479,7 +2478,7 @@ fn addPathForDynLibs( const dll_path = try maker.generatedPath(conf_comp.generated_bin.value.?).toString(arena); const search_path = Dir.path.dirname(dll_path).?; if (environ_map.get(path_key)) |prev_path| { - const new_path = try allocPrint(arena, "{s}{c}{s}", .{ prev_path, path_delimiter, search_path }); + const new_path = try arena.print("{s}{c}{s}", .{ prev_path, path_delimiter, search_path }); try environ_map.put(path_key, new_path); } else { try environ_map.put(path_key, search_path); diff --git a/lib/compiler/Maker/Step/TranslateC.zig b/lib/compiler/Maker/Step/TranslateC.zig index 11b30e89b6ec6a677e04df4622bee15a3a4c0bd2..94b90d65dc9a8ec48871d945924cb0e4461e80f4 100644 --- a/lib/compiler/Maker/Step/TranslateC.zig +++ b/lib/compiler/Maker/Step/TranslateC.zig @@ -3,7 +3,6 @@ const TranslateC = @This(); const std = @import("std"); const Io = std.Io; const Configuration = std.Build.Configuration; -const allocPrint = std.fmt.allocPrint; const assert = std.debug.assert; const OptimizeMode = std.lang.OptimizeMode; @@ -54,7 +53,7 @@ pub fn make( .fast => .ReleaseFast, .small => .ReleaseSmall, }; - if (opt) |o| argv.appendAssumeCapacity(try allocPrint(arena, "-O{t}", .{o})); + if (opt) |o| argv.appendAssumeCapacity(try arena.print("-O{t}", .{o})); try argv.ensureUnusedCapacity(arena, conf_tc.include_dirs.len * 2); for (0..conf_tc.include_dirs.len) |i| @@ -133,7 +132,7 @@ pub fn make( else => |e| return e, } } - try argv.append(arena, try allocPrint(arena, "{s}{s}", .{ + try argv.append(arena, try arena.print("{s}{s}", .{ prefix, system_lib_name, })); } @@ -151,7 +150,7 @@ pub fn make( }).?; const stem = Io.Dir.path.stem(Io.Dir.path.basename(c_source_path)); - const out_basename = try allocPrint(arena, "{s}.zig", .{stem}); + const out_basename = try arena.print("{s}.zig", .{stem}); maker.generatedPath(conf_tc.output_file).* = try output_dir_path.join(arena, out_basename); } diff --git a/lib/compiler/Maker/Step/UpdateSourceFiles.zig b/lib/compiler/Maker/Step/UpdateSourceFiles.zig index d33ff091d157bfcc601031253e6310f04c18a38a..dde19e0ce865930dba1972a5a8e5670665efb8f9 100644 --- a/lib/compiler/Maker/Step/UpdateSourceFiles.zig +++ b/lib/compiler/Maker/Step/UpdateSourceFiles.zig @@ -3,7 +3,6 @@ const UpdateSourceFiles = @This(); const std = @import("std"); const Io = std.Io; const Path = std.Build.Cache.Path; -const allocPrint = std.fmt.allocPrint; const Configuration = std.Build.Configuration; const Step = @import("../Step.zig"); diff --git a/lib/compiler/Maker/Step/WriteFile.zig b/lib/compiler/Maker/Step/WriteFile.zig index ca14f46fada5fd1106e7ad53d552c521ab36bd79..53bd71eda99ce058926660731b581f682e13c8ef 100644 --- a/lib/compiler/Maker/Step/WriteFile.zig +++ b/lib/compiler/Maker/Step/WriteFile.zig @@ -4,7 +4,6 @@ const std = @import("std"); const Io = std.Io; const assert = std.debug.assert; const Path = std.Build.Cache.Path; -const allocPrint = std.fmt.allocPrint; const Configuration = std.Build.Configuration; const Step = @import("../Step.zig"); diff --git a/lib/std/Build/Step/ConfigHeader.zig b/lib/std/Build/Step/ConfigHeader.zig index c16d6273abe6668ef272172c6b8649739da9af49..9d036dcf2db7b1417c60af507461d8ec647aeb9e 100644 --- a/lib/std/Build/Step/ConfigHeader.zig +++ b/lib/std/Build/Step/ConfigHeader.zig @@ -5,7 +5,6 @@ const Io = std.Io; const Step = std.Build.Step; const Allocator = std.mem.Allocator; const Configuration = std.Build.Configuration; -const allocPrint = std.fmt.allocPrint; step: Step, values: std.array_hash_map.String(Value) = .empty, @@ -84,13 +83,9 @@ pub fn create(owner: *std.Build, options: Options) *ConfigHeader { }; const name = if (options.style.getPath()) |s| - allocPrint(arena, "configure {t} header {f} to {s}", .{ - options.style, s, include_path, - }) catch @panic("OOM") + arena.print("configure {t} header {f} to {s}", .{ options.style, s, include_path }) catch @panic("OOM") else - allocPrint(arena, "configure {t} header to {s}", .{ - options.style, include_path, - }) catch @panic("OOM"); + arena.print("configure {t} header to {s}", .{ options.style, include_path }) catch @panic("OOM"); config_header.* = .{ .step = .init(.{ diff --git a/lib/std/Build/Step/TranslateC.zig b/lib/std/Build/Step/TranslateC.zig index 5698111e044860f573a9dcfc9fcb5ba5393c8590..be205bee22b83395a364d9f962f5545888597ada 100644 --- a/lib/std/Build/Step/TranslateC.zig +++ b/lib/std/Build/Step/TranslateC.zig @@ -3,7 +3,6 @@ const TranslateC = @This(); const std = @import("std"); const fs = std.fs; const mem = std.mem; -const allocPrint = std.fmt.allocPrint; const Step = std.Build.Step; const LazyPath = std.Build.LazyPath; const Configuration = std.Build.Configuration; @@ -158,7 +157,7 @@ pub fn defineCMacro(translate_c: *TranslateC, name: []const u8, value: ?[]const const graph = translate_c.step.owner.graph; const arena = graph.arena; const wc = &graph.wip_configuration; - const macro = allocPrint(arena, "{s}={s}", .{ name, value orelse "1" }) catch @panic("OOM"); + const macro = arena.print("{s}={s}", .{ name, value orelse "1" }) catch @panic("OOM"); const macro_string = wc.addString(macro) catch @panic("OOM"); translate_c.c_macros.append(arena, macro_string) catch @panic("OOM"); } diff --git a/src/link/Lld.zig b/src/link/Lld.zig index c4c4e0fb3d721cc134473db4b348ee65a048d08e..715c0ba69f0c8f50ad6306ad7519c173a4d3dc0e 100644 --- a/src/link/Lld.zig +++ b/src/link/Lld.zig @@ -436,23 +436,23 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { try argv.append("-DEBUG"); const out_ext = std.fs.path.extension(full_out_path); - const out_pdb = coff.pdb_out_path orelse try allocPrint(arena, "{s}.pdb", .{ + const out_pdb = coff.pdb_out_path orelse try arena.print("{s}.pdb", .{ full_out_path[0 .. full_out_path.len - out_ext.len], }); const out_pdb_basename = std.fs.path.basename(out_pdb); - try argv.append(try allocPrint(arena, "-PDB:{s}", .{out_pdb})); - try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb_basename})); + try argv.append(try arena.print("-PDB:{s}", .{out_pdb})); + try argv.append(try arena.print("-PDBALTPATH:{s}", .{out_pdb_basename})); } if (comp.version) |version| { - try argv.append(try allocPrint(arena, "-VERSION:{d}.{d}", .{ version.major, version.minor })); + try argv.append(try arena.print("-VERSION:{d}.{d}", .{ version.major, version.minor })); } if (target_util.llvmMachineAbi(target)) |mabi| { - try argv.append(try allocPrint(arena, "-MLLVM:-target-abi={s}", .{mabi})); + try argv.append(try arena.print("-MLLVM:-target-abi={s}", .{mabi})); } - try argv.append(try allocPrint(arena, "-MLLVM:-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"})); + try argv.append(try arena.print("-MLLVM:-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"})); if (comp.config.lto != .none) { switch (optimize_mode) { @@ -462,9 +462,9 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { } } if (comp.config.output_mode == .Exe) { - try argv.append(try allocPrint(arena, "-STACK:{d}", .{base.stack_size})); + try argv.append(try arena.print("-STACK:{d}", .{base.stack_size})); } - try argv.append(try allocPrint(arena, "-BASE:{d}", .{coff.image_base})); + try argv.append(try arena.print("-BASE:{d}", .{coff.image_base})); switch (base.build_id) { .none => try argv.append("-BUILD-ID:NO"), @@ -483,7 +483,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { } for (comp.force_undefined_symbols.keys()) |symbol| { - try argv.append(try allocPrint(arena, "-INCLUDE:{s}", .{symbol})); + try argv.append(try arena.print("-INCLUDE:{s}", .{symbol})); } if (is_dyn_lib) { @@ -491,7 +491,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { } if (entry_name) |name| { - try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{name})); + try argv.append(try arena.print("-ENTRY:{s}", .{name})); } if (coff.repro) { @@ -511,26 +511,26 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { try argv.append("-FORCE:UNRESOLVED"); } - try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path})); + try argv.append(try arena.print("-OUT:{s}", .{full_out_path})); if (comp.emit_implib) |raw_emit_path| { const path = try comp.resolveEmitPathFlush(arena, .artifact, raw_emit_path); - try argv.append(try allocPrint(arena, "-IMPLIB:{f}", .{path})); + try argv.append(try arena.print("-IMPLIB:{f}", .{path})); } if (comp.config.link_libc) { if (comp.libc_installation) |libc_installation| { - try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?})); + try argv.append(try arena.print("-LIBPATH:{s}", .{libc_installation.crt_dir.?})); if (target.abi == .msvc or target.abi == .itanium) { - try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?})); - try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?})); + try argv.append(try arena.print("-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?})); + try argv.append(try arena.print("-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?})); } } } for (coff.lib_directories) |lib_directory| { - try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_directory.path orelse "."})); + try argv.append(try arena.print("-LIBPATH:{s}", .{lib_directory.path orelse "."})); } try argv.ensureUnusedCapacity(comp.link_inputs.len); @@ -541,7 +541,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { }, .object, .archive => |obj| { if (obj.must_link) { - argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{f}", .{@as(Cache.Path, obj.path)})); + argv.appendAssumeCapacity(try arena.print("-WHOLEARCHIVE:{f}", .{@as(Cache.Path, obj.path)})); } else { argv.appendAssumeCapacity(try obj.path.toString(arena)); } @@ -561,7 +561,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { } if (coff.module_definition_file) |def| { - try argv.append(try allocPrint(arena, "-DEF:{s}", .{def})); + try argv.append(try arena.print("-DEF:{s}", .{def})); } const resolved_subsystem: ?std.zig.Subsystem = blk: { @@ -590,7 +590,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { const Mode = enum { uefi, win32 }; const mode: Mode = mode: { if (resolved_subsystem) |subsystem| { - try argv.append(try allocPrint(arena, "-SUBSYSTEM:{s},{d}.{d}", .{ + try argv.append(try arena.print("-SUBSYSTEM:{s},{d}.{d}", .{ @tagName(subsystem), coff.major_subsystem_version, coff.minor_subsystem_version, @@ -645,8 +645,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { .static => "lib", .dynamic => "", }; - try argv.append(try allocPrint(arena, "{s}vcruntime.lib", .{lib_str})); - try argv.append(try allocPrint(arena, "{s}ucrt.lib", .{lib_str})); + try argv.append(try arena.print("{s}vcruntime.lib", .{lib_str})); + try argv.append(try arena.print("{s}ucrt.lib", .{lib_str})); //Visual C++ 2015 Conformance Changes //https://msdn.microsoft.com/en-us/library/bb531344.aspx @@ -712,7 +712,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { try argv.ensureUnusedCapacity(comp.windows_libs.count()); for (comp.windows_libs.keys()) |key| { - const lib_basename = try allocPrint(arena, "{s}.lib", .{key}); + const lib_basename = try arena.print("{s}.lib", .{key}); if (comp.crt_files.get(lib_basename)) |crt_file| { argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena)); continue; @@ -722,7 +722,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { continue; } if (target.abi.isGnu()) { - const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key}); + const fallback_name = try arena.print("lib{s}.dll.a", .{key}); if (try findLib(arena, io, fallback_name, coff.lib_directories)) |full_path| { argv.appendAssumeCapacity(full_path); continue; @@ -843,19 +843,19 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { try argv.append("--error-limit=0"); if (comp.sysroot) |sysroot| { - try argv.append(try std.fmt.allocPrint(arena, "--sysroot={s}", .{sysroot})); + try argv.append(try arena.print("--sysroot={s}", .{sysroot})); } if (target_util.llvmMachineAbi(target)) |mabi| { try argv.appendSlice(&.{ "-mllvm", - try std.fmt.allocPrint(arena, "-target-abi={s}", .{mabi}), + try arena.print("-target-abi={s}", .{mabi}), }); } try argv.appendSlice(&.{ "-mllvm", - try std.fmt.allocPrint(arena, "-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"}), + try arena.print("-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"}), }); switch (target.cpu.arch) { @@ -894,19 +894,19 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { if (output_mode == .Exe) { try argv.appendSlice(&.{ "-z", - try std.fmt.allocPrint(arena, "stack-size={d}", .{base.stack_size}), + try arena.print("stack-size={d}", .{base.stack_size}), }); } switch (base.build_id) { .none => try argv.append("--build-id=none"), - .fast, .uuid, .sha1, .md5 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{ + .fast, .uuid, .sha1, .md5 => try argv.append(try arena.print("--build-id={s}", .{ @tagName(base.build_id), })), - .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{x}", .{hs.toSlice()})), + .hexstring => |hs| try argv.append(try arena.print("--build-id=0x{x}", .{hs.toSlice()})), } - try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{elf.image_base})); + try argv.append(try arena.print("--image-base={d}", .{elf.image_base})); if (elf.linker_script) |linker_script| { try argv.append("-T"); @@ -914,7 +914,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { } if (elf.sort_section) |how| { - const arg = try std.fmt.allocPrint(arena, "--sort-section={s}", .{@tagName(how)}); + const arg = try arena.print("--sort-section={s}", .{@tagName(how)}); try argv.append(arg); } @@ -980,11 +980,11 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { } if (elf.z_common_page_size) |size| { try argv.append("-z"); - try argv.append(try std.fmt.allocPrint(arena, "common-page-size={d}", .{size})); + try argv.append(try arena.print("common-page-size={d}", .{size})); } if (elf.z_max_page_size) |size| { try argv.append("-z"); - try argv.append(try std.fmt.allocPrint(arena, "max-page-size={d}", .{size})); + try argv.append(try arena.print("max-page-size={d}", .{size})); } if (getLDMOption(target)) |ldm| { @@ -1190,7 +1190,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { if (target.os.versionRange().gnuLibCVersion().?.order(rem_in) != .lt) continue; } - const lib_path = try std.fmt.allocPrint(arena, "{f}{c}lib{s}.so.{d}", .{ + const lib_path = try arena.print("{f}{c}lib{s}.so.{d}", .{ comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, }); try argv.append(lib_path); @@ -1207,21 +1207,21 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { if (target.os.version_range.semver.min.order(add_in) == .lt) continue; } - const lib_path = try std.fmt.allocPrint(arena, "{f}{c}lib{s}.so.{d}", .{ + const lib_path = try arena.print("{f}{c}lib{s}.so.{d}", .{ comp.freebsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.getSoVersion(&target.os), }); try argv.append(lib_path); } } else if (target.isNetBSDLibC()) { for (netbsd.libs) |lib| { - const lib_path = try std.fmt.allocPrint(arena, "{f}{c}lib{s}.so.{d}", .{ + const lib_path = try arena.print("{f}{c}lib{s}.so.{d}", .{ comp.netbsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, }); try argv.append(lib_path); } } else if (target.isOpenBSDLibC()) { for (openbsd.libs) |lib| { - const lib_path = try std.fmt.allocPrint(arena, "{f}{c}lib{s}.so", .{ + const lib_path = try arena.print("{f}{c}lib{s}.so", .{ comp.openbsd_so_files.?.dir_path, fs.path.sep, lib.name, }); try argv.append(lib_path); @@ -1451,12 +1451,12 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { } if (wasm.initial_memory) |initial_memory| { - const arg = try std.fmt.allocPrint(arena, "--initial-memory={d}", .{initial_memory}); + const arg = try arena.print("--initial-memory={d}", .{initial_memory}); try argv.append(arg); } if (wasm.max_memory) |max_memory| { - const arg = try std.fmt.allocPrint(arena, "--max-memory={d}", .{max_memory}); + const arg = try arena.print("--max-memory={d}", .{max_memory}); try argv.append(arg); } @@ -1465,7 +1465,7 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { } if (wasm.global_base) |global_base| { - const arg = try std.fmt.allocPrint(arena, "--global-base={d}", .{global_base}); + const arg = try arena.print("--global-base={d}", .{global_base}); try argv.append(arg); } else { // We prepend it by default, so when a stack overflow happens the runtime will trap correctly, @@ -1477,7 +1477,7 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { // Users are allowed to specify which symbols they want to export to the wasm host. for (wasm.export_symbol_names) |symbol_name| { - const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name}); + const arg = try arena.print("--export={s}", .{symbol_name}); try argv.append(arg); } @@ -1493,15 +1493,15 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { try argv.appendSlice(&.{ "-z", - try std.fmt.allocPrint(arena, "stack-size={d}", .{base.stack_size}), + try arena.print("stack-size={d}", .{base.stack_size}), }); switch (base.build_id) { .none => try argv.append("--build-id=none"), - .fast, .uuid, .sha1 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{ + .fast, .uuid, .sha1 => try argv.append(try arena.print("--build-id={s}", .{ @tagName(base.build_id), })), - .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{x}", .{hs.toSlice()})), + .hexstring => |hs| try argv.append(try arena.print("--build-id=0x{x}", .{hs.toSlice()})), .md5 => {}, } @@ -1685,7 +1685,7 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi .argv = &.{ argv[0], argv[1], - try std.fmt.allocPrint(arena, "@{s}", .{ + try arena.print("@{s}", .{ try comp.dirs.local_cache.join(arena, &.{rsp_path}), }), }, @@ -1740,7 +1740,6 @@ const std = @import("std"); const Io = std.Io; const Allocator = std.mem.Allocator; const Cache = std.Build.Cache; -const allocPrint = std.fmt.allocPrint; const assert = std.debug.assert; const fs = std.fs; const log = std.log.scoped(.link); diff --git a/src/main.zig b/src/main.zig index 47bf6d2f49517e235cb86ded97a665d8c5c5192e..7ddb635c87944ffc2c40bcccd31fbc6eeed20eba 100644 --- a/src/main.zig +++ b/src/main.zig @@ -21,7 +21,6 @@ const AstGen = std.zig.AstGen; const ZonGen = std.zig.ZonGen; const Server = std.zig.Server; const stringToEnum = std.meta.stringToEnum; -const allocPrint = std.fmt.allocPrint; pub const tracy = @import("tracy.zig"); const Compilation = @import("Compilation.zig"); @@ -2964,7 +2963,7 @@ fn buildOutputType( while (preprocessor_args_it.next()) |arg| { if (mem.eql(u8, arg, "-MD") or mem.eql(u8, arg, "-MMD") or mem.eql(u8, arg, "-MT")) { disable_c_depfile = true; - const cc_arg = try allocPrint(arena, "-Wp,{s},{s}", .{ arg, preprocessor_args_it.nextOrFatal() }); + const cc_arg = try arena.print("-Wp,{s},{s}", .{ arg, preprocessor_args_it.nextOrFatal() }); try cc_argv.append(arena, cc_arg); } else { fatal("unsupported preprocessor arg: {s}", .{arg}); @@ -3408,9 +3407,9 @@ fn buildOutputType( .yes_default_value => if (create_module.resolved_options.output_mode == .Lib and create_module.resolved_options.link_mode == .dynamic and target.ofmt == .elf) if (have_version) - try allocPrint(arena, "lib{s}.so.{d}", .{ root_name, version.major }) + try arena.print("lib{s}.so.{d}", .{ root_name, version.major }) else - try allocPrint(arena, "lib{s}.so", .{root_name}) + try arena.print("lib{s}.so", .{root_name}) else null, }; @@ -3420,7 +3419,7 @@ fn buildOutputType( .yes_default_path => emit: { if (output_to_cache != null) break :emit .yes_cache; const name = switch (clang_preprocessor_mode) { - .pch => try allocPrint(arena, "{s}.pch", .{root_name}), + .pch => try arena.print("{s}.pch", .{root_name}), else => try std.zig.binNameAlloc(arena, .{ .root_name = root_name, .cpu_arch = target.cpu.arch, @@ -3456,16 +3455,16 @@ fn buildOutputType( }, }; - const default_h_basename = try allocPrint(arena, "{s}.h", .{root_name}); + const default_h_basename = try arena.print("{s}.h", .{root_name}); const emit_h_resolved = emit_h.resolve(io, default_h_basename, output_to_cache); - const default_asm_basename = try allocPrint(arena, "{s}.s", .{root_name}); + const default_asm_basename = try arena.print("{s}.s", .{root_name}); const emit_asm_resolved = emit_asm.resolve(io, default_asm_basename, output_to_cache); - const default_llvm_ir_basename = try allocPrint(arena, "{s}.ll", .{root_name}); + const default_llvm_ir_basename = try arena.print("{s}.ll", .{root_name}); const emit_llvm_ir_resolved = emit_llvm_ir.resolve(io, default_llvm_ir_basename, output_to_cache); - const default_llvm_bc_basename = try allocPrint(arena, "{s}.bc", .{root_name}); + const default_llvm_bc_basename = try arena.print("{s}.bc", .{root_name}); const emit_llvm_bc_resolved = emit_llvm_bc.resolve(io, default_llvm_bc_basename, output_to_cache); const emit_docs_resolved = emit_docs.resolve(io, "docs", output_to_cache); @@ -3486,7 +3485,7 @@ fn buildOutputType( fatal("the argument -femit-implib is allowed only when building a Windows DLL", .{}); } } - const default_implib_basename = try allocPrint(arena, "{s}.lib", .{root_name}); + const default_implib_basename = try arena.print("{s}.lib", .{root_name}); const emit_implib_resolved: Compilation.CreateOptions.Emit = switch (emit_implib) { .no => .no, .yes => emit_implib.resolve(io, default_implib_basename, output_to_cache), @@ -3515,7 +3514,7 @@ fn buildOutputType( // "-" is stdin. Dump it to a real file. const sep = fs.path.sep_str; - const dump_path = try allocPrint(arena, "tmp" ++ sep ++ "{x}-dump-stdin{s}", .{ + const dump_path = try arena.print("tmp" ++ sep ++ "{x}-dump-stdin{s}", .{ randInt(io, u64), ext.canonicalName(target), }); try dirs.local_cache.handle.createDirPath(io, "tmp"); @@ -3544,7 +3543,7 @@ fn buildOutputType( const bin_digest: Cache.BinDigest = hasher.hasher.finalResult(); - const sub_path = try allocPrint(arena, "tmp" ++ sep ++ "{x}-stdin{s}", .{ + const sub_path = try arena.print("tmp" ++ sep ++ "{x}-stdin{s}", .{ &bin_digest, ext.canonicalName(target), }); try dirs.local_cache.handle.rename(dump_path, dirs.local_cache.handle, sub_path, io); @@ -3876,9 +3875,9 @@ fn buildOutputType( for (mod.cc_argv) |cc_arg| test_exec_args.appendAssumeCapacity(cc_arg); for (mod.deps) |dep| try test_exec_args.appendSlice(arena, &.{ "--dep", - if (std.mem.eql(u8, dep.key, dep.value)) dep.value else try std.fmt.allocPrint(arena, "{s}={s}", .{ dep.key, dep.value }), + if (std.mem.eql(u8, dep.key, dep.value)) dep.value else try arena.print("{s}={s}", .{ dep.key, dep.value }), }); - try test_exec_args.append(arena, try std.fmt.allocPrint(arena, "-M{s}", .{mod_name})); + try test_exec_args.append(arena, try arena.print("-M{s}", .{mod_name})); } try test_exec_args.ensureUnusedCapacity(arena, comp.global_cc_argv.len); @@ -4573,7 +4572,7 @@ fn runOrTest( try argv.append(exe_path); if (arg_mode == .zig_test) { try argv.append( - try allocPrint(arena, "--seed=0x{x}", .{randInt(io, u32)}), + try arena.print("--seed=0x{x}", .{randInt(io, u32)}), ); } } else { @@ -4781,7 +4780,7 @@ fn cmdTranslateC( assert(comp.c_source_files.len == 1); const c_source_file = comp.c_source_files[0]; - const translated_basename = try allocPrint(arena, "{s}.zig", .{comp.root_name}); + const translated_basename = try arena.print("{s}.zig", .{comp.root_name}); var man: Cache.Manifest = comp.obtainCObjectCacheManifest(comp.root_mod); man.want_shared_lock = false; @@ -4890,7 +4889,7 @@ fn jitCmd( const root_prog_node = std.Progress.start(io, .{ .disable_printing = (color == .off), - .root_name = try allocPrint(arena, "Compiling {s} (first time setup)", .{options.cmd_name}), + .root_name = try arena.print("Compiling {s} (first time setup)", .{options.cmd_name}), }); defer root_prog_node.end(); @@ -5053,13 +5052,13 @@ fn jitCmdInner( if (options.prepend_cmd) |cmd| child_argv.appendAssumeCapacity(cmd); if (options.prepend_zig_lib_dir_path) - child_argv.appendAssumeCapacity(try allocPrint(arena, "--zig-lib={s}", .{dirs.zig_lib.path.?})); + child_argv.appendAssumeCapacity(try arena.print("--zig-lib={s}", .{dirs.zig_lib.path.?})); if (options.prepend_zig_exe_path) - child_argv.appendAssumeCapacity(try allocPrint(arena, "--zig={s}", .{self_exe_path})); + child_argv.appendAssumeCapacity(try arena.print("--zig={s}", .{self_exe_path})); if (options.prepend_global_cache_path) - child_argv.appendAssumeCapacity(try allocPrint(arena, "--global-cache={s}", .{dirs.global_cache.path.?})); + child_argv.appendAssumeCapacity(try arena.print("--global-cache={s}", .{dirs.global_cache.path.?})); if (options.prepend_seed) - child_argv.appendAssumeCapacity(try allocPrint(arena, "--seed=0x{x}", .{randInt(io, u32)})); + child_argv.appendAssumeCapacity(try arena.print("--seed=0x{x}", .{randInt(io, u32)})); child_argv.appendSliceAssumeCapacity(args); -- 2.54.0 From f7e36077e4c616f0560c13bcc80ab056ecf71859 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 25 Jun 2026 13:30:38 -0700 Subject: [PATCH 39/49] maker gets its own ZIG_DEBUG_MAKER env var it can be useful to only set this and not the other one, or vice versa --- ci/x86_64-linux-debug.sh | 2 +- lib/std/zig.zig | 1 + src/main.zig | 6 ++++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ci/x86_64-linux-debug.sh b/ci/x86_64-linux-debug.sh index 91f6291d90b345139f394f4ee0c26bbe80a5ac57..0316d3bef37f4058d256c97b62ae0e212160aead 100755 --- a/ci/x86_64-linux-debug.sh +++ b/ci/x86_64-linux-debug.sh @@ -44,7 +44,7 @@ ninja install # Must be done after zig cc is finished. export ZIG_LIB_DIR="$PWD/../lib" -export ZIG_DEBUG_CMD=1 +export ZIG_DEBUG_MAKER=1 # simultaneously test building self-hosted without LLVM and with 32-bit arm stage3-debug/bin/zig build \ diff --git a/lib/std/zig.zig b/lib/std/zig.zig index c875663d67e028ecccfc6e5cac6a9647fc4d7d26..cf6a1c3eb8131de1c16e4bcd2d227460ab78e52a 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -782,6 +782,7 @@ pub const EnvVar = enum { ZIG_VERBOSE_LINK, ZIG_VERBOSE_CC, ZIG_DEBUG_CMD, + ZIG_DEBUG_MAKER, ZIG_IS_DETECTING_LIBC_PATHS, ZIG_IS_AVOIDING_CALLING_ITSELF, diff --git a/src/main.zig b/src/main.zig index 7ddb635c87944ffc2c40bcccd31fbc6eeed20eba..39652f0996e5ddc6e69d24e603006eed9646ca80 100644 --- a/src/main.zig +++ b/src/main.zig @@ -360,6 +360,7 @@ fn mainArgs( .prepend_global_cache_path = true, .prepend_zig_exe_path = true, .prepend_seed = true, + .debug_env_var = .ZIG_DEBUG_MAKER, }); }, .clang, .@"-cc1", .@"-cc1as" => { @@ -4873,6 +4874,7 @@ const JitCmdOptions = struct { capture: ?*[]u8 = null, /// Send error bundles via std.zig.Server over stdout server: bool = false, + debug_env_var: EnvVar = .ZIG_DEBUG_CMD, }; fn jitCmd( @@ -4923,7 +4925,7 @@ fn jitCmdInner( const self_exe_path = process.executablePathAlloc(io, arena) catch |err| fatal("unable to find self exe path: {t}", .{err}); - const optimize_mode: std.lang.OptimizeMode = if (EnvVar.ZIG_DEBUG_CMD.isSet(environ_map)) + const optimize_mode: std.lang.OptimizeMode = if (options.debug_env_var.isSet(environ_map)) .Debug else .ReleaseFast; @@ -5062,7 +5064,7 @@ fn jitCmdInner( child_argv.appendSliceAssumeCapacity(args); - if (EnvVar.ZIG_DEBUG_CMD.isSet(environ_map)) { + if (options.debug_env_var.isSet(environ_map)) { const cmd: std.zig.SubprocessCommand = .{ .argv = child_argv.items, }; -- 2.54.0 From 266ffd41334f046d866adba5cf6c0457dbd86240 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 25 Jun 2026 13:40:29 -0700 Subject: [PATCH 40/49] Maker: build it in ReleaseSafe mode by default also, introduce ZIG_VERBOSE_CMD for printing the child argv rather than repurposing ZIG_DEBUG_CMD because the stderr output can be problematic --- lib/std/zig.zig | 1 + src/main.zig | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/std/zig.zig b/lib/std/zig.zig index cf6a1c3eb8131de1c16e4bcd2d227460ab78e52a..4ab3364d6e2e6421ba7ccc66a23d6c5895583341 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -781,6 +781,7 @@ pub const EnvVar = enum { ZIG_BUILD_MULTILINE_ERRORS, ZIG_VERBOSE_LINK, ZIG_VERBOSE_CC, + ZIG_VERBOSE_CMD, ZIG_DEBUG_CMD, ZIG_DEBUG_MAKER, ZIG_IS_DETECTING_LIBC_PATHS, diff --git a/src/main.zig b/src/main.zig index 39652f0996e5ddc6e69d24e603006eed9646ca80..dbe4c34265ea7cba91200748d35c2cd99df427f8 100644 --- a/src/main.zig +++ b/src/main.zig @@ -361,6 +361,7 @@ fn mainArgs( .prepend_zig_exe_path = true, .prepend_seed = true, .debug_env_var = .ZIG_DEBUG_MAKER, + .release_mode = .ReleaseSafe, }); }, .clang, .@"-cc1", .@"-cc1as" => { @@ -4875,6 +4876,7 @@ const JitCmdOptions = struct { /// Send error bundles via std.zig.Server over stdout server: bool = false, debug_env_var: EnvVar = .ZIG_DEBUG_CMD, + release_mode: std.lang.OptimizeMode = .ReleaseFast, }; fn jitCmd( @@ -4928,7 +4930,7 @@ fn jitCmdInner( const optimize_mode: std.lang.OptimizeMode = if (options.debug_env_var.isSet(environ_map)) .Debug else - .ReleaseFast; + options.release_mode; const strip = optimize_mode != .Debug; const override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map); const override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map); @@ -5064,7 +5066,7 @@ fn jitCmdInner( child_argv.appendSliceAssumeCapacity(args); - if (options.debug_env_var.isSet(environ_map)) { + if (EnvVar.ZIG_VERBOSE_CMD.isSet(environ_map)) { const cmd: std.zig.SubprocessCommand = .{ .argv = child_argv.items, }; -- 2.54.0 From 2821a58fcda853487531ed054d64b5271869b79b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 25 Jun 2026 13:57:12 -0700 Subject: [PATCH 41/49] update resinator and std-docs to new jitCmd prefix args --- lib/compiler/resinator/main.zig | 6 +++--- lib/compiler/std-docs.zig | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/compiler/resinator/main.zig b/lib/compiler/resinator/main.zig index 170a6c02d3606a93c43c85901157da4c967bb0d3..862ef30b0bb62225dff6a86c008f2cebfaa548b4 100644 --- a/lib/compiler/resinator/main.zig +++ b/lib/compiler/resinator/main.zig @@ -44,7 +44,7 @@ pub fn main(init: std.process.Init.Minimal) !void { try renderErrorMessage(stderr.terminal(), .err, "expected zig lib dir as first argument", .{}); std.process.exit(1); } - const zig_lib_dir = args[1]; + const zig_lib_dir = std.mem.cutPrefix(u8, args[1], "--zig-lib=") orelse @panic("bad --zig-lib= arg"); var cli_args = args[2..]; var zig_integration = false; @@ -639,7 +639,7 @@ fn getIncludePaths( }; const target = std.zig.resolveTargetQueryOrFatal(io, target_query); const is_native_abi = target_query.isNativeAbi(); - const detected_libc = std.zig.LibCDirs.detect(arena, io, .{ .root_dir = .cwd, .sub_path = zig_lib_dir }, &target, is_native_abi, true, null, environ_map) catch { + const detected_libc = std.zig.LibCDirs.detect(arena, io, .{ .root_dir = .cwd(), .sub_path = zig_lib_dir }, &target, is_native_abi, true, null, environ_map) catch { if (includes == .any) { // fall back to mingw includes = .gnu; @@ -668,7 +668,7 @@ fn getIncludePaths( const detected_libc = std.zig.LibCDirs.detect( arena, io, - .{ .root_dir = .cwd, .sub_path = zig_lib_dir }, + .{ .root_dir = .cwd(), .sub_path = zig_lib_dir }, &target, is_native_abi, true, diff --git a/lib/compiler/std-docs.zig b/lib/compiler/std-docs.zig index db2d22a06f02b999e408b0609635277d95284a70..f41159adea3c864e0c02fe2402bbba70196dd91d 100644 --- a/lib/compiler/std-docs.zig +++ b/lib/compiler/std-docs.zig @@ -29,9 +29,9 @@ pub fn main(init: std.process.Init) !void { var argv = try init.minimal.args.iterateAllocator(arena); defer argv.deinit(); assert(argv.skip()); - const zig_lib_directory = argv.next().?; - const zig_exe_path = argv.next().?; - const global_cache_path = argv.next().?; + const zig_lib_directory = mem.cutPrefix(u8, argv.next().?, "--zig-lib=") orelse @panic("bad --zig-lib= arg"); + const zig_exe_path = mem.cutPrefix(u8, argv.next().?, "--zig=") orelse @panic("bad --zig= arg"); + const global_cache_path = mem.cutPrefix(u8, argv.next().?, "--global-cache=") orelse @panic("bad --global-cache= arg"); var lib_dir = try Io.Dir.cwd().openDir(io, zig_lib_directory, .{}); defer lib_dir.close(io); -- 2.54.0 From 7f9851c0d8630c6ed10fa591ef520b38c4a16f00 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 25 Jun 2026 13:57:57 -0700 Subject: [PATCH 42/49] zig init: use the provided args rather than redundant searching --- lib/compiler/Maker.zig | 43 +++++++++++++++--------------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 8066db7a6224a0e8e1e12443bf35bc1b60561c83..6af5c1b27aac1c6f46fa70a31de9df2d08f75dd6 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -1791,7 +1791,7 @@ fn cmdInit(gpa: Allocator, graph: *Graph, args: []const []const u8) !void { switch (template) { .example => { - var templates = findTemplates(gpa, arena, io); + var templates = Templates.find(gpa, io, graph.zig_lib_directory); defer templates.deinit(io); const s = Dir.path.sep_str; @@ -3640,6 +3640,20 @@ const Templates = struct { .flags = .{ .exclusive = true }, }); } + + fn find(gpa: Allocator, io: Io, zig_lib_directory: Cache.Directory) Templates { + const template_path: Path = .{ + .root_dir = zig_lib_directory, + .sub_path = "init", + }; + const template_dir = template_path.root_dir.handle.openDir(io, template_path.sub_path, .{}) catch |err| + fatal("unable to open zig project template directory {f}: {t}", .{ template_path, err }); + return .{ + .zig_lib_directory = zig_lib_directory, + .dir = template_dir, + .buffer = std.array_list.Managed(u8).init(gpa), + }; + } }; fn writeSimpleTemplateFile(io: Io, file_name: []const u8, comptime format: []const u8, args: anytype) !void { const f = try Io.Dir.cwd().createFile(io, file_name, .{ .exclusive = true }); @@ -3649,30 +3663,3 @@ fn writeSimpleTemplateFile(io: Io, file_name: []const u8, comptime format: []con try fw.interface.print(format, args); try fw.interface.flush(); } - -fn findTemplates(gpa: Allocator, arena: Allocator, io: Io) Templates { - const cwd_path = std.zig.getResolvedCwd(io, arena) catch |err| { - fatal("unable to get cwd: {t}", .{err}); - }; - const self_exe_path = process.executablePathAlloc(io, arena) catch |err| { - fatal("unable to find self exe path: {t}", .{err}); - }; - var zig_lib_directory = std.zig.findZigLibDirFromSelfExe(arena, io, cwd_path, self_exe_path) catch |err| { - fatal("unable to find zig installation directory {q}: {t}", .{ self_exe_path, err }); - }; - - const s = Dir.path.sep_str; - const template_sub_path = "init"; - const template_dir = zig_lib_directory.handle.openDir(io, template_sub_path, .{}) catch |err| { - const path = zig_lib_directory.path orelse "."; - fatal("unable to open zig project template directory '{s}{s}{s}': {t}", .{ - path, s, template_sub_path, err, - }); - }; - - return .{ - .zig_lib_directory = zig_lib_directory, - .dir = template_dir, - .buffer = std.array_list.Managed(u8).init(gpa), - }; -} -- 2.54.0 From 2554d416dfe2d5bf1df4de256b7c761ba464984d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 26 Jun 2026 00:46:44 -0700 Subject: [PATCH 43/49] Maker: raise file descriptor limit This was present in the corresponding cmdBuild code. --- lib/compiler/Maker.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 6af5c1b27aac1c6f46fa70a31de9df2d08f75dd6..47a0211a069f440d2e8953346f5998dd7f62ee38 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -135,6 +135,7 @@ pub fn main(init: process.Init.Minimal) !void { // The build runner is long-lived in the following use cases: // * `--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) { @@ -552,6 +553,8 @@ pub fn main(init: process.Init.Minimal) !void { 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}); -- 2.54.0 From 7004ceff01b952567313546f368d70781cf5dbb4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 26 Jun 2026 10:50:28 -0700 Subject: [PATCH 44/49] ci: don't set zig lib dir for zig cc --- ci/x86_64-linux-release.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ci/x86_64-linux-release.sh b/ci/x86_64-linux-release.sh index 0fbccca8d3291869b1d68e461417053524b2b109..64c6589fba220c76e25e9d80f950efc97f777df4 100755 --- a/ci/x86_64-linux-release.sh +++ b/ci/x86_64-linux-release.sh @@ -48,7 +48,7 @@ unset CXX ninja install -# Must be done after zig cc is finished. +# Must not be set while using the other `zig cc` which has its own zig lib dir. export ZIG_LIB_DIR="$PWD/../lib" # Covers several things: @@ -99,6 +99,7 @@ cd ../build-new export CC="$ZIG cc -target $TARGET -mcpu=$MCPU" export CXX="$ZIG c++ -target $TARGET -mcpu=$MCPU" +unset ZIG_LIB_DIR cmake .. \ -DCMAKE_PREFIX_PATH="$PREFIX" \ @@ -117,6 +118,8 @@ unset CXX ninja install +export ZIG_LIB_DIR="$PWD/../lib" + stage3/bin/zig test ../test/behavior.zig stage3/bin/zig build -p stage4 \ -Dstatic-llvm \ -- 2.54.0 From 2ef3f63e1ddc2c5c6b1e1881a54d4922770a2133 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 26 Jun 2026 15:00:20 -0700 Subject: [PATCH 45/49] Fetch: tweak error messages --- lib/compiler/Maker/Fetch.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compiler/Maker/Fetch.zig b/lib/compiler/Maker/Fetch.zig index 7d89556b9be0733f4709643e8956f02695a519d6..da5103b0a7ae410407f04c29c3a344bcf0f2f8a3 100644 --- a/lib/compiler/Maker/Fetch.zig +++ b/lib/compiler/Maker/Fetch.zig @@ -783,7 +783,7 @@ fn runResource( f.package_root = try ls.pkg_root.join(arena, computed_package_hash.toSlice()); renameTmpIntoCache(io, package_sub_path, f.package_root) catch |err| { try eb.addRootErrorMessage(.{ .msg = try eb.printString( - "failed renaming temporary directory {f} into package cache directory {f}: {t}", + "failed to rename temporary directory {f} into package cache directory {f}: {t}", .{ package_sub_path, f.package_root, err }, ) }); return error.FetchFailed; @@ -803,7 +803,7 @@ fn runResource( if (!package_sub_path.eql(tmp_directory_path)) { tmp_directory_path.root_dir.handle.deleteDir(io, tmp_directory_path.sub_path) catch |err| switch (err) { error.Canceled => |e| return e, - else => |e| log.warn("failed deleting temporary directory {f}: {t}", .{ tmp_directory_path, e }), + else => |e| log.warn("failed to delete temporary directory {f}: {t}", .{ tmp_directory_path, e }), }; } @@ -827,7 +827,7 @@ fn runResource( }); const notes_start = try eb.reserveNotes(notes_len); eb.extra.items[notes_start] = @intFromEnum(try eb.addErrorMessage(.{ - .msg = try eb.printString("expected .hash = \"{s}\",", .{computed_package_hash.toSlice()}), + .msg = try eb.printString("expected .hash = {q},", .{computed_package_hash.toSlice()}), })); return error.FetchFailed; } -- 2.54.0 From 3b2af05df10a92a0ce6ec70e2f926dfddc2d4ba9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 26 Jun 2026 20:03:36 -0700 Subject: [PATCH 46/49] std.Build: prefer {q} over '{s}' --- lib/std/Build.zig | 46 ++++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 84dd3f84169b776c3e5197ee02979af30d7eb143..e349aba6c21549626289a837845a8c45092d0796 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -852,10 +852,7 @@ pub fn addModule(b: *Build, name: []const u8, options: Module.CreateOptions) *Mo module, ) catch @panic("OOM"); if (gop.found_existing) { - panic( - "A module with the name '{s}' has already been added to the package. Consider creating a private module with std.Build.createModule", - .{name}, - ); + panic("A module with the name {q} has already been added to the package. Consider creating a private module with std.Build.createModule", .{name}); } return module; } @@ -1016,7 +1013,7 @@ pub fn addNamedWriteFiles(b: *Build, name: []const u8) *Step.WriteFile { ) catch @panic("OOM"); if (gop.found_existing) { panic( - "A WriteFile step with the name '{s}' has already been added to the package. Consider creating a private WriteFile step with std.Build.addWriteFiles", + "A WriteFile step with the name {q} has already been added to the package. Consider creating a private WriteFile step with std.Build.addWriteFiles", .{name}, ); } @@ -1031,10 +1028,7 @@ pub fn addNamedLazyPath(b: *Build, name: []const u8, lp: LazyPath) void { lp.dupe(graph), ) catch @panic("OOM"); if (gop.found_existing) { - panic( - "A LazyPath with the name '{s}' has already been added to the package.", - .{name}, - ); + panic("A LazyPath with the name {q} has already been added to the package.", .{name}); } } @@ -1137,7 +1131,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw .enum_options = enum_options, }; if ((b.available_options_map.fetchPut(arena, name, available_option) catch @panic("OOM")) != null) { - panic("option '{s}' declared twice", .{name}); + panic("option {q} declared twice", .{name}); } const option_ptr = b.user_input_options.getPtr(name) orelse return null; @@ -1389,7 +1383,7 @@ pub fn parseTargetQuery(options: std.Target.Query.ParseOptions) error{ParseFaile opts_copy.diagnostics = &diags; return std.Target.Query.parse(opts_copy) catch |err| switch (err) { error.UnknownCpuModel => { - std.debug.print("unknown CPU: '{s}'\navailable CPUs for architecture '{t}':\n", .{ + std.debug.print("unknown CPU: {q}\navailable CPUs for architecture {t}:\n", .{ diags.cpu_name.?, diags.arch.?, }); for (diags.arch.?.allCpuModels()) |cpu| { @@ -1399,7 +1393,7 @@ pub fn parseTargetQuery(options: std.Target.Query.ParseOptions) error{ParseFaile }, error.UnknownCpuFeature => { std.debug.print( - \\unknown CPU feature: '{s}' + \\unknown CPU feature: {q} \\available CPU features for architecture '{t}': \\ , .{ @@ -1412,7 +1406,7 @@ pub fn parseTargetQuery(options: std.Target.Query.ParseOptions) error{ParseFaile }, error.UnknownOperatingSystem => { std.debug.print( - \\unknown OS: '{s}' + \\unknown OS: {q} \\available operating systems: \\ , .{diags.os_name.?}); @@ -1422,9 +1416,7 @@ pub fn parseTargetQuery(options: std.Target.Query.ParseOptions) error{ParseFaile return error.ParseFailed; }, else => |e| { - std.debug.print("unable to parse target '{s}': {s}\n", .{ - options.arch_os_abi, @errorName(e), - }); + std.debug.print("unable to parse target {q}: {t}\n", .{ options.arch_os_abi, e }); return error.ParseFailed; }, }; @@ -1487,7 +1479,7 @@ pub fn standardTargetOptionsQueryOnly(b: *Build, args: StandardTargetOptionsArgs q.serializeCpuAlloc(arena) catch @panic("OOM"), }); } - log.err("chosen target '{s}' does not match one of the allowed targets", .{ + log.err("chosen target {q} does not match one of the allowed targets", .{ selected_target.zigTriple(arena) catch @panic("OOM"), }); b.markInvalidUserInput(); @@ -1542,7 +1534,7 @@ pub fn addUserInputOption(b: *Build, name_raw: []const u8, value_raw: []const u8 return true; }, .lazy_path, .lazy_path_list => { - log.warn("the lazy path value type isn't added from the CLI, but somehow '{s}' is a .{f}", .{ + log.warn("the lazy path value type isn't added from the CLI, but somehow {q} is a .{f}", .{ name, std.zig.fmtId(@tagName(gop.value_ptr.value)), }); return true; @@ -1710,9 +1702,7 @@ pub fn addCheckFile( /// References a file or directory relative to the source root. pub fn path(b: *Build, sub_path: []const u8) LazyPath { if (fs.path.isAbsolute(sub_path)) { - panic("sub_path is expected to be relative to the build root, but was this absolute path: '{s}'. Absolute paths can cause problems but can be created via Graph.cwdRelativePath", .{ - sub_path, - }); + panic("sub_path is expected to be relative to the build root, but was this absolute path: {q}. Absolute paths can cause problems but can be created via Graph.cwdRelativePath", .{sub_path}); } return .{ .src_path = .{ .owner = b, @@ -2033,34 +2023,34 @@ pub const Dependency = struct { for (d.builder.install_tls.step.dependencies.items) |dep_step| { const inst = dep_step.cast(Step.InstallArtifact) orelse continue; if (mem.eql(u8, inst.artifact.name, name)) { - if (found != null) panic("artifact name '{s}' is ambiguous", .{name}); + if (found != null) panic("artifact name {q} is ambiguous", .{name}); found = inst.artifact; } } return found orelse { for (d.builder.install_tls.step.dependencies.items) |dep_step| { const inst = dep_step.cast(Step.InstallArtifact) orelse continue; - log.info("available artifact: '{s}'", .{inst.artifact.name}); + log.info("available artifact: {q}", .{inst.artifact.name}); } - panic("unable to find artifact '{s}'", .{name}); + panic("unable to find artifact {q}", .{name}); }; } pub fn module(d: *Dependency, name: []const u8) *Module { return d.builder.modules.get(name) orelse { - panic("unable to find module '{s}'", .{name}); + panic("unable to find module {q}", .{name}); }; } pub fn namedWriteFiles(d: *Dependency, name: []const u8) *Step.WriteFile { return d.builder.named_writefiles.get(name) orelse { - panic("unable to find named writefiles '{s}'", .{name}); + panic("unable to find named writefiles {q}", .{name}); }; } pub fn namedLazyPath(d: *Dependency, name: []const u8) LazyPath { return d.builder.named_lazy_paths.get(name) orelse { - panic("unable to find named lazypath '{s}'", .{name}); + panic("unable to find named lazypath {q}", .{name}); }; } @@ -2628,7 +2618,7 @@ fn dumpBadDirnameHelp( if (asking_step) |as| { stderr.setColor(.red) catch {}; - try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name}); + try w.print(" The step {q} that is missing a dependency on the above step was created by this stack trace:\n", .{as.name}); stderr.setColor(.reset) catch {}; as.dump(stderr); -- 2.54.0 From cc9463e45782e469f3d3282f9abc900fb3978d4e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 29 Jun 2026 16:58:23 -0700 Subject: [PATCH 47/49] Maker: avoid printing configure cmdline on compilation errors --- lib/compiler/Maker.zig | 1 + lib/std/zig.zig | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 47a0211a069f440d2e8953346f5998dd7f62ee38..c9e739d53e136496c0c8e6aa33d1cb3eeaf4a5fc 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -1237,6 +1237,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { .cache_manifest = &config_man, .arch_os_abi = target_arch_os_abi, .progress_node = compile_prog_node, + .skip_log_cmdline_on_compile_errors = !graph.verbose, })) |r| r.path else |err| return err; defer gpa.free(configure_exe_path.sub_path); diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 4ab3364d6e2e6421ba7ccc66a23d6c5895583341..52e5f6c2608fce8fc49ccff006d786da91a6775e 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -1637,6 +1637,7 @@ pub const BuildExeSubprocessOptions = struct { arch_os_abi: ?[]const u8 = null, cpu_features: ?[]const u8 = null, progress_node: std.Progress.Node = .none, + skip_log_cmdline_on_compile_errors: bool = false, }; pub const BuildExeSubprocessError = error{ @@ -1820,7 +1821,9 @@ pub fn buildExeSubprocess( return error.AlreadyReported; }, }; - log.err("command reported {d} compilation errors: {f}", .{ result_error_bundle.errorMessageCount(), cmd }); + if (!options.skip_log_cmdline_on_compile_errors) log.err("command reported {d} compilation errors: {f}", .{ + result_error_bundle.errorMessageCount(), cmd, + }); if (received_fs_inputs) return error.FailedButCacheIntact; return error.AlreadyReported; } -- 2.54.0 From fc7924d393c909fef785614b8273e719398db2c1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 29 Jun 2026 17:01:46 -0700 Subject: [PATCH 48/49] Maker: fatal rather than panic for unimplemented feature When user passes --watch or --webui but has build.zig compile errors, instead of panicking it will explain there is an unimplemented build system feature. --- lib/compiler/Maker.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index c9e739d53e136496c0c8e6aa33d1cb3eeaf4a5fc..d35a9dd53751001fdcace80f7ece432baf4f185b 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -847,10 +847,10 @@ pub fn main(init: process.Init.Minimal) !void { _ = io.lockStderr(&.{}, graph.stderr_mode) catch {}; process.exit(1); } - if (can_fs_watch) { - @panic("TODO set up fs watching"); + if (watch and can_fs_watch) { + fatal("(zig build system) TODO set up fs watching even when build.zig compilation fails", .{}); } else { - @panic("TODO wait for user to request rebuild"); + fatal("(zig build system) TODO stay running and wait for user to request rebuild even when build.zig compilation fails", .{}); } } } -- 2.54.0 From 779b6cc63cbf1ab0f132996506a238eae72dcbf9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 29 Jun 2026 23:48:35 -0700 Subject: [PATCH 49/49] Maker: partially implement configurer cache invalidation progress towards #35473 --- build.zig | 2 +- lib/compiler/Maker.zig | 61 +++++++++++++++++++++++++++------ lib/compiler/Maker/Graph.zig | 1 - lib/compiler/configurer.zig | 5 ++- lib/std/Build.zig | 1 + lib/std/Build/Cache.zig | 34 ++++++++++++------ lib/std/Build/Configuration.zig | 8 +---- 7 files changed, 78 insertions(+), 34 deletions(-) diff --git a/build.zig b/build.zig index 19bf41dc607d3b93e96bf53598d9945f8bd5bcf4..c98dcc649ca0773a12490c2d43dddc1ff344b873 100644 --- a/build.zig +++ b/build.zig @@ -268,7 +268,7 @@ pub fn build(b: *std.Build) !void { } // Ensure git version changes get picked up. - b.dependOnFileContents(b.graph.path(.build_root, ".git/HEAD")); + b.dependOnFileContents(b.path(".git/HEAD")); const version_string = b.fmt("{d}.{d}.{d}", .{ zig_version.major, zig_version.minor, zig_version.patch }); diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index d35a9dd53751001fdcace80f7ece432baf4f185b..46653431cb447f1d773c6838681a06e7c1c26cf0 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -1318,7 +1318,11 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { } for (configuration.path_deps) |path_dep| { - try config_man.addPathPost(path_dep.toCachePath(&configuration, arena)); + switch (path_dep.flags.mode) { + .directory => {}, // TODO + .contents => try config_man.addPathPost(confPathDepToCachePath(graph, &configuration, path_dep)), + .metadata => {}, // TODO + } } // If it is poisoned, there is no point in moving it to cached @@ -1825,7 +1829,7 @@ fn cmdInit(gpa: Allocator, graph: *Graph, args: []const []const u8) !void { return process.cleanExit(io); }, .minimal => { - writeSimpleTemplateFile(io, Package.Manifest.basename, + Templates.writeSimpleFile(io, Package.Manifest.basename, \\.{{ \\ .name = .{s}, \\ .version = "0.0.1", @@ -1842,7 +1846,7 @@ fn cmdInit(gpa: Allocator, graph: *Graph, args: []const []const u8) !void { else => fatal("failed to create {q}: {t}", .{ Package.Manifest.basename, err }), error.PathAlreadyExists => fatal("refusing to overwrite {q}", .{Package.Manifest.basename}), }; - writeSimpleTemplateFile(io, default_build_zig_basename, + Templates.writeSimpleFile(io, default_build_zig_basename, \\const std = @import("std"); \\ \\pub fn build(b: *std.Build) void {{ @@ -3498,7 +3502,7 @@ fn loadManifest( 0, ) catch |err| switch (err) { error.FileNotFound => { - writeSimpleTemplateFile(io, Package.Manifest.basename, + Templates.writeSimpleFile(io, Package.Manifest.basename, \\.{{ \\ .name = .{s}, \\ .version = "{s}", @@ -3658,12 +3662,47 @@ const Templates = struct { .buffer = std.array_list.Managed(u8).init(gpa), }; } + + fn writeSimpleFile(io: Io, file_name: []const u8, comptime format: []const u8, args: anytype) !void { + const f = try Io.Dir.cwd().createFile(io, file_name, .{ .exclusive = true }); + defer f.close(io); + var buf: [4096]u8 = undefined; + var fw = f.writer(io, &buf); + try fw.interface.print(format, args); + try fw.interface.flush(); + } }; -fn writeSimpleTemplateFile(io: Io, file_name: []const u8, comptime format: []const u8, args: anytype) !void { - const f = try Io.Dir.cwd().createFile(io, file_name, .{ .exclusive = true }); - defer f.close(io); - var buf: [4096]u8 = undefined; - var fw = f.writer(io, &buf); - try fw.interface.print(format, args); - try fw.interface.flush(); + +fn confPathDepToCachePath(graph: *const Graph, c: *const Configuration, path_dep: Configuration.PathDep) Path { + const sub_path = path_dep.sub.slice(c); + return switch (path_dep.flags.base) { + .cwd => .{ + .root_dir = .cwd(), + .sub_path = sub_path, + }, + .local_cache => .{ + .root_dir = graph.local_cache_root, + .sub_path = sub_path, + }, + .global_cache => .{ + .root_dir = graph.global_cache_root, + .sub_path = sub_path, + }, + .build_root => .{ + .root_dir = switch (path_dep.pkg.unwrap().?) { + .root => graph.build_root_directory, + _ => @panic("TODO"), + }, + .sub_path = sub_path, + }, + .zig_lib => .{ + .root_dir = graph.zig_lib_directory, + .sub_path = sub_path, + }, + .zig_exe => @panic("TODO"), + .install_prefix => @panic("TODO"), + .install_lib => @panic("TODO"), + .install_bin => @panic("TODO"), + .install_include => @panic("TODO"), + }; } diff --git a/lib/compiler/Maker/Graph.zig b/lib/compiler/Maker/Graph.zig index 116132c614e740ed7159c276141759d2f0e8f1ab..fabe31d5367c5611bd69612b9c61669af1ec4d12 100644 --- a/lib/compiler/Maker/Graph.zig +++ b/lib/compiler/Maker/Graph.zig @@ -4,7 +4,6 @@ const Graph = @This(); const std = @import("std"); const Io = std.Io; const Allocator = std.mem.Allocator; -const Configuration = std.Build.Configuration; const Path = std.Build.Cache.Path; const Directory = std.Build.Cache.Directory; diff --git a/lib/compiler/configurer.zig b/lib/compiler/configurer.zig index a7b7ae44274356702ffe34923d39405d23715f11..809bb9e6263c3a9c0e128bb6a677589fef6054c9 100644 --- a/lib/compiler/configurer.zig +++ b/lib/compiler/configurer.zig @@ -633,8 +633,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { var s: Serialize = .{ .wc = wc, .arena = arena }; try wc.path_deps.ensureTotalCapacityPrecise(gpa, graph.configure_dependencies.items.len); - // TODO remove this - if (false) for ( + for ( graph.configure_dependencies.items, wc.path_deps.addManyAsSliceAssumeCapacity(graph.configure_dependencies.items.len), ) |src, *dest| { @@ -662,7 +661,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { .dependency => |d| .init(try s.builderToPackage(d.dependency.builder)), }, }; - }; + } // Starting from all top-level steps in `b`, traverse the entire step graph // and add all step dependencies implied by module graphs. diff --git a/lib/std/Build.zig b/lib/std/Build.zig index e349aba6c21549626289a837845a8c45092d0796..6d07c0fb49c6a056151a8fdc0a4adc13aa385dc1 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -177,6 +177,7 @@ pub const Graph = struct { /// A path whose components and contents are known at some point during /// `Step` resolution, relative to the provided base directory. pub fn path(graph: *Graph, base: Configuration.LazyPath.Relative.Base, sub_path: []const u8) LazyPath { + assert(base != .build_root); return .{ .relative = .{ .base = base, .sub_path = @This().dupePath(graph, sub_path), diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 54f0b8baad329214e94a37d56173f04d04c4d082..f731f5667c30b6406f0b3721d1249b1b3437fd77 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -70,6 +70,15 @@ pub const PrefixedPath = struct { } }; +fn findPrefixPath(cache: *const Cache, path: Path) !PrefixedPath { + const gpa = cache.gpa; + const resolved_path = try std.fs.path.resolve(gpa, &.{ + cache.cwd, path.root_dir.path orelse ".", path.subPathOrDot(), + }); + errdefer gpa.free(resolved_path); + return findPrefixResolved(cache, resolved_path); +} + fn findPrefix(cache: *const Cache, file_path: []const u8) !PrefixedPath { const gpa = cache.gpa; const resolved_path = try std.fs.path.resolve(gpa, &.{file_path}); @@ -998,13 +1007,22 @@ pub const Manifest = struct { /// This is useful for processes that don't know the all the files that are /// depended on ahead of time. For example, a source file that can import /// other files will need to be recompiled if the imported file is changed. - pub fn addFilePost(self: *Manifest, file_path: []const u8) !void { - assert(self.manifest_file != null); - const gpa = self.cache.gpa; - const prefixed_path = try self.cache.findPrefix(file_path); + pub fn addFilePost(man: *Manifest, file_path: []const u8) !void { + assert(man.manifest_file != null); + const gpa = man.cache.gpa; + const prefixed_path = try man.cache.findPrefix(file_path); var keep = false; defer if (!keep) gpa.free(prefixed_path.sub_path); - keep = try addPrefixedPathPost(self, prefixed_path); + keep = try addPrefixedPathPost(man, prefixed_path); + } + + pub fn addPathPost(man: *Manifest, path: Path) !void { + assert(man.manifest_file != null); + const gpa = man.cache.gpa; + const prefixed_path: PrefixedPath = try man.cache.findPrefixPath(path); + var keep = false; + defer if (!keep) gpa.free(prefixed_path.sub_path); + keep = try addPrefixedPathPost(man, prefixed_path); } /// Low level function. `prefixed_path` references cloned memory. Returns @@ -1034,12 +1052,6 @@ pub const Manifest = struct { return true; } - pub fn addPathPost(man: *Manifest, path: Path) !void { - _ = man; - _ = path; - std.log.err("TODO Build.Cache.addPathPost", .{}); - } - /// Like `addFilePost` but when the file contents have already been loaded from disk. pub fn addFilePostContents( self: *Manifest, diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig index 4a0fc763e043705f120e6d317c59b406ba1a9afd..f10bf9055b58d63061dfa3b37ff7954474a35dcf 100644 --- a/lib/std/Build/Configuration.zig +++ b/lib/std/Build/Configuration.zig @@ -1557,6 +1557,7 @@ pub const LazyPath = union(@This().Tag) { cwd, local_cache, global_cache, + /// Must not be used with Relative since package index is missing. build_root, zig_exe, zig_lib, @@ -1876,13 +1877,6 @@ pub const PathDep = extern struct { }; pub const Mode = enum(u8) { directory, contents, metadata }; - - pub fn toCachePath(path: PathDep, c: *const Configuration, arena: Allocator) std.Build.Cache.Path { - _ = c; - _ = arena; - _ = path; - if (true) @panic("TODO Configuration.PathDep.toCachePath"); - } }; pub const InstallDestDir = enum(u32) { -- 2.54.0