From 70ea17b91df8f67ae0a5514f0da85d567d2048aa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 8 Mar 2026 22:42:41 -0700 Subject: [PATCH] maker: progress towards lowering Compile Step CLI args next thing to do is figure out how LazyPath is supposed to work now. something like this: * each Step that provides LazyPath objects has a setLazyPath and getLazyPath function which takes a tagged union identifying which one to access * steps that fulfill LazyPath objects can freely call setLazyPath without obtaining a lock because the dependency graph prevents simultaneous access. * similarly, steps that access LazyPath results can freely call getLazyPath without obtaining a lock, because after modification, there may be simultaneous reads from dependencies but they will all be read-only * a fulfilled LazyPath object is a read-only std.Build.Cache.Path. --- lib/compiler/Maker/ScannedConfig.zig | 2 +- lib/compiler/Maker/Step.zig | 9 +- lib/compiler/Maker/Step/Compile.zig | 696 +++++++++++++++------------ lib/compiler/configurer.zig | 1 + lib/std/Build/Module.zig | 8 +- lib/std/zig/Configuration.zig | 31 +- 6 files changed, 437 insertions(+), 310 deletions(-) diff --git a/lib/compiler/Maker/ScannedConfig.zig b/lib/compiler/Maker/ScannedConfig.zig index 3be476931a..3e5e5f950b 100644 --- a/lib/compiler/Maker/ScannedConfig.zig +++ b/lib/compiler/Maker/ScannedConfig.zig @@ -135,7 +135,7 @@ fn printValue(sc: *const ScannedConfig, s: *Serializer, comptime Field: type, fi .extended => @compileError("TODO"), .union_list => { var slice_field = try s.beginTuple(.{}); - for (field_value.get(c.extra), 0..) |elem, i| switch (field_value.tag(c.extra, i)) { + for (field_value.slice(c.extra), 0..) |elem, i| switch (field_value.tag(c.extra, i)) { inline else => |tag| { var sub_struct = try s.beginStruct(.{}); try sub_struct.fieldPrefix(@tagName(tag)); diff --git a/lib/compiler/Maker/Step.zig b/lib/compiler/Maker/Step.zig index 0115f3a7db..aaed3a7ac2 100644 --- a/lib/compiler/Maker/Step.zig +++ b/lib/compiler/Maker/Step.zig @@ -316,13 +316,14 @@ pub fn captureChildProcess( return result; } -pub fn fail(step: *Step, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, MakeFailed } { - try step.addError(fmt, args); +pub fn fail(step: *Step, maker: *const Maker, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, MakeFailed } { + try step.addError(maker, fmt, args); return error.MakeFailed; } -pub fn addError(step: *Step, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void { - const arena = step.owner.allocator; +pub fn addError(step: *Step, maker: *const Maker, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void { + const graph = maker.graph; + const arena = graph.arena; // TODO don't leak into the process_arena const msg = try std.fmt.allocPrint(arena, fmt, args); try step.result_error_msgs.append(arena, msg); } diff --git a/lib/compiler/Maker/Step/Compile.zig b/lib/compiler/Maker/Step/Compile.zig index 5ace2335fa..d67236d883 100644 --- a/lib/compiler/Maker/Step/Compile.zig +++ b/lib/compiler/Maker/Step/Compile.zig @@ -23,14 +23,17 @@ zig_args: std.ArrayList([]const u8) = .empty, pub fn make( compile: *Compile, - step_index: Configuration.Step.Index, + compile_index: Configuration.Step.Index, maker: *Maker, progress_node: std.Progress.Node, ) Step.ExtendedMakeError!void { const graph = maker.graph; - const step = maker.stepByIndex(step_index); + const step = maker.stepByIndex(compile_index); + + // Reset / repopulate persistent state. compile.zig_args.clearRetainingCapacity(); - try lowerZigArgs(compile, step_index, maker, &compile.zig_args, false); + + try lowerZigArgs(compile, compile_index, maker, &compile.zig_args, false); if (true) @panic("TODO implement compile.make()"); const process_arena = graph.arena; // TODO don't leak into the process_arena @@ -42,7 +45,7 @@ pub fn make( ) catch |err| switch (err) { error.NeedCompileErrorCheck => { assert(compile.expect_errors != null); - try checkCompileErrors(compile); + try checkCompileErrors(compile, maker); return; }, else => |e| return e, @@ -79,19 +82,50 @@ pub fn make( } } +/// List of importable modules in a compilation's module graph, including +/// the root module. The root module is guaranteed to be first. +const ModuleList = std.AutoArrayHashMapUnmanaged(Configuration.Module.Index, Configuration.String); +/// Keyed on the first key in the module list. +const ModuleGraph = std.ArrayHashMapUnmanaged(ModuleList, void, ModuleListContext, false); + +const ModuleListContext = struct { + pub fn eql(ctx: @This(), a: ModuleList, b: ModuleList) bool { + _ = ctx; + return a.keys()[0] == b.keys()[0]; + } + + pub fn hash(ctx: @This(), key: ModuleList) u32 { + _ = ctx; + return std.hash.int(@intFromEnum(key.keys()[0])); + } + + const Adapter = struct { + pub fn eql(ctx: @This(), a: Configuration.Module.Index, b: ModuleList, b_index: usize) bool { + _ = ctx; + _ = b_index; + return a == b.keys()[0]; + } + + pub fn hash(ctx: @This(), key: Configuration.Module.Index) u32 { + _ = ctx; + return std.hash.int(@intFromEnum(key)); + } + }; +}; + fn lowerZigArgs( - compile: *Compile, - step_index: Configuration.Step.Index, - maker: *Maker, + compile: *const Compile, + compile_index: Configuration.Step.Index, + maker: *const Maker, zig_args: *std.ArrayList([]const u8), fuzz: bool, -) Allocator.Error!void { - const step = maker.stepByIndex(step_index); +) error{ OutOfMemory, MakeFailed }!void { + const step = maker.stepByIndex(compile_index); const graph = maker.graph; const arena = graph.arena; // TODO don't leak into the process arena const gpa = maker.gpa; const conf = &maker.scanned_config.configuration; - const conf_step = step_index.ptr(conf); + const conf_step = compile_index.ptr(conf); const conf_comp = conf_step.extended.get(conf.extra).compile; try zig_args.append(gpa, graph.zig_exe); @@ -142,21 +176,21 @@ fn lowerZigArgs( try addBool(gpa, zig_args, "-ffuzz", fuzz); - if (true) @panic("TODO"); - - var is_linking_libc = conf_comp.flags3.is_linking_libc; - var is_linking_libcpp = conf_comp.flags3.is_linking_libcpp; - { + var is_linking_libc = conf_comp.flags3.is_linking_libc; + var is_linking_libcpp = conf_comp.flags3.is_linking_libcpp; + // Stores system libraries that have already been seen for at least one - // module, along with any arguments that need to be passed to the - // compiler for each module individually. - var seen_system_libs: std.StringHashMapUnmanaged([]const []const u8) = .empty; - var frameworks: std.StringArrayHashMapUnmanaged(Module.FrameworkFlags) = .empty; + // module, along with any C compiler arguments that need to be passed + // to the compiler for each module individually as reported by + // pkg-config. + var seen_system_libs: std.AutoArrayHashMapUnmanaged(Configuration.String, []const []const u8) = .empty; + var frameworks: std.AutoArrayHashMapUnmanaged(Configuration.String, Configuration.Module.Framework.Flags) = .empty; + var module_graph: ModuleGraph = .empty; var prev_has_cflags = false; var prev_has_rcflags = false; - var prev_search_strategy: Module.SystemLib.SearchStrategy = .paths_first; + var prev_search_strategy: Configuration.SystemLib.SearchStrategy = .paths_first; var prev_preferred_link_mode: std.builtin.LinkMode = .dynamic; // Track the number of positional arguments so that a nice error can be // emitted if there is nothing to link. @@ -164,250 +198,256 @@ fn lowerZigArgs( // Fully recursive iteration including dynamic libraries to detect // libc and libc++ linkage. - for (getCompileDependencies(true)) |some_compile| { - for (some_compile.root_module.getGraph().modules) |mod| { - if (mod.link_libc == true) is_linking_libc = true; - if (mod.link_libcpp == true) is_linking_libcpp = true; + for (try getCompileDependencies(arena, &module_graph, conf, compile_index, true)) |some_compile_index| { + const some_compile = some_compile_index.ptr(conf).extended.get(conf.extra).compile; + const modules = try getModuleList(arena, &module_graph, some_compile.root_module, conf); + for (modules.keys()) |mod_index| { + const mod = mod_index.get(conf); + is_linking_libc = is_linking_libc or mod.flags2.link_libc == .true; + is_linking_libcpp = is_linking_libcpp or mod.flags2.link_libcpp == .true; } } - var cli_named_modules = try CliNamedModules.init(arena, compile.root_module); + var cli_named_modules = try CliNamedModules.init(arena, &module_graph, compile_index, maker); // For this loop, don't chase dynamic libraries because their link // objects are already linked. - for (getCompileDependencies(false)) |dep_compile| { - for (dep_compile.root_module.getGraph().modules) |mod| { + for (try getCompileDependencies(arena, &module_graph, conf, compile_index, false)) |dep_compile_index| { + const dep_compile = dep_compile_index.ptr(conf).extended.get(conf.extra).compile; + const modules = try getModuleList(arena, &module_graph, dep_compile.root_module, conf); + for (modules.keys()) |mod_index| { + const mod = mod_index.get(conf); // While walking transitive dependencies, if a given link object is // already included in a library, it should not redundantly be // placed on the linker line of the dependee. - const my_responsibility = dep_compile == compile; + const my_responsibility = dep_compile_index == compile_index; const already_linked = !my_responsibility and dep_compile.isDynamicLibrary(); // Inherit dependencies on darwin frameworks. if (!already_linked) { - for (mod.frameworks.keys(), mod.frameworks.values()) |name, info| { - try frameworks.put(arena, name, info); + for (mod.frameworks.slice) |framework| { + try frameworks.put(arena, framework.name, framework.flags); } } + if (true) @panic("TODO"); + // Inherit dependencies on system libraries and static libraries. - for (mod.link_objects.items) |link_object| { - switch (link_object) { - .static_path => |static_path| { - if (my_responsibility) { - try zig_args.append(gpa, static_path.getPath2(step)); - total_linker_objects += 1; - } - }, - .system_lib => |system_lib| { - const system_lib_gop = try seen_system_libs.getOrPut(arena, system_lib.name); - if (system_lib_gop.found_existing) { - try zig_args.appendSlice(gpa, system_lib_gop.value_ptr.*); - continue; - } else { - system_lib_gop.value_ptr.* = &.{}; - } + for (0..mod.link_objects.len) |lo_i| switch (mod.link_objects.get(conf.extra, lo_i)) { + .static_path => |static_path| { + if (my_responsibility) { + try zig_args.append(gpa, static_path.getPath2(step)); + total_linker_objects += 1; + } + }, + .system_lib => |system_lib| { + const system_lib_gop = try seen_system_libs.getOrPut(arena, system_lib.name); + if (system_lib_gop.found_existing) { + try zig_args.appendSlice(gpa, system_lib_gop.value_ptr.*); + continue; + } else { + system_lib_gop.value_ptr.* = &.{}; + } - if (already_linked) - continue; + if (already_linked) + continue; - if ((system_lib.search_strategy != prev_search_strategy or - system_lib.preferred_link_mode != prev_preferred_link_mode) and - compile.linkage != .static) - { - switch (system_lib.search_strategy) { - .no_fallback => switch (system_lib.preferred_link_mode) { - .dynamic => try zig_args.append(gpa, "-search_dylibs_only"), - .static => try zig_args.append(gpa, "-search_static_only"), - }, - .paths_first => switch (system_lib.preferred_link_mode) { - .dynamic => try zig_args.append(gpa, "-search_paths_first"), - .static => try zig_args.append(gpa, "-search_paths_first_static"), - }, - .mode_first => switch (system_lib.preferred_link_mode) { - .dynamic => try zig_args.append(gpa, "-search_dylibs_first"), - .static => try zig_args.append(gpa, "-search_static_first"), - }, - } - prev_search_strategy = system_lib.search_strategy; - prev_preferred_link_mode = system_lib.preferred_link_mode; + if ((system_lib.search_strategy != prev_search_strategy or + system_lib.preferred_link_mode != prev_preferred_link_mode) and + compile.linkage != .static) + { + switch (system_lib.search_strategy) { + .no_fallback => switch (system_lib.preferred_link_mode) { + .dynamic => try zig_args.append(gpa, "-search_dylibs_only"), + .static => try zig_args.append(gpa, "-search_static_only"), + }, + .paths_first => switch (system_lib.preferred_link_mode) { + .dynamic => try zig_args.append(gpa, "-search_paths_first"), + .static => try zig_args.append(gpa, "-search_paths_first_static"), + }, + .mode_first => switch (system_lib.preferred_link_mode) { + .dynamic => try zig_args.append(gpa, "-search_dylibs_first"), + .static => try zig_args.append(gpa, "-search_static_first"), + }, } + prev_search_strategy = system_lib.search_strategy; + prev_preferred_link_mode = system_lib.preferred_link_mode; + } - const prefix: []const u8 = prefix: { - if (system_lib.needed) break :prefix "-needed-l"; - if (system_lib.weak) break :prefix "-weak-l"; - break :prefix "-l"; - }; - switch (system_lib.use_pkg_config) { - .no => try zig_args.append(gpa, try allocPrint(arena, "{s}{s}", .{ prefix, system_lib.name })), - .yes, .force => { - if (compile.runPkgConfig(maker, system_lib.name)) |result| { - try zig_args.appendSlice(gpa, result.cflags); - try zig_args.appendSlice(gpa, result.libs); - try seen_system_libs.put(arena, system_lib.name, result.cflags); - } else |err| switch (err) { - error.PkgConfigInvalidOutput, - error.PkgConfigCrashed, - error.PkgConfigFailed, - error.PkgConfigNotInstalled, - error.PackageNotFound, - => switch (system_lib.use_pkg_config) { - .yes => { - // pkg-config failed, so fall back to linking the library - // by name directly. - try zig_args.append(gpa, try allocPrint(arena, "{s}{s}", .{ - prefix, - system_lib.name, - })); - }, - .force => { - return step.fail("pkg-config failed for library {s}", .{system_lib.name}); - }, - .no => unreachable, + const prefix: []const u8 = prefix: { + if (system_lib.needed) break :prefix "-needed-l"; + if (system_lib.weak) break :prefix "-weak-l"; + break :prefix "-l"; + }; + switch (system_lib.use_pkg_config) { + .no => try zig_args.append(gpa, try allocPrint(arena, "{s}{s}", .{ prefix, system_lib.name })), + .yes, .force => { + if (compile.runPkgConfig(maker, system_lib.name)) |result| { + try zig_args.appendSlice(gpa, result.cflags); + try zig_args.appendSlice(gpa, result.libs); + try seen_system_libs.put(arena, system_lib.name, result.cflags); + } else |err| switch (err) { + error.PkgConfigInvalidOutput, + error.PkgConfigCrashed, + error.PkgConfigFailed, + error.PkgConfigNotInstalled, + error.PackageNotFound, + => switch (system_lib.use_pkg_config) { + .yes => { + // pkg-config failed, so fall back to linking the library + // by name directly. + try zig_args.append(gpa, try allocPrint(arena, "{s}{s}", .{ + prefix, + system_lib.name, + })); }, + .force => { + return step.fail(maker, "pkg-config failed for library {s}", .{system_lib.name}); + }, + .no => unreachable, + }, - else => |e| return e, - } - }, - } - }, - .other_step => |other| { - switch (other.kind) { - .exe => return step.fail("cannot link with an executable build artifact", .{}), - .@"test" => return step.fail("cannot link with a test", .{}), - .obj, .test_obj => { - const included_in_lib_or_obj = !my_responsibility and - (dep_compile.kind == .lib or dep_compile.kind == .obj or dep_compile.kind == .test_obj); - if (!already_linked and !included_in_lib_or_obj) { - try zig_args.append(gpa, other.getEmittedBin().getPath2(step)); - total_linker_objects += 1; - } - }, - .lib => l: { - const other_produces_implib = other.producesImplib(); - const other_is_static = other_produces_implib or other.isStaticLibrary(); - - if (compile.isStaticLibrary() and other_is_static) { - // Avoid putting a static library inside a static library. - break :l; - } - - // For DLLs, we must link against the implib. - // For everything else, we directly link - // against the library file. - const full_path_lib = if (other_produces_implib) - try other.getGeneratedFilePath("generated_implib", &compile.step) - else - try other.getGeneratedFilePath("generated_bin", &compile.step); - - try zig_args.append(gpa, full_path_lib); + else => |e| return e, + } + }, + } + }, + .other_step => |other| { + switch (other.kind) { + .exe => return step.fail(maker, "cannot link with an executable build artifact", .{}), + .@"test" => return step.fail(maker, "cannot link with a test", .{}), + .obj, .test_obj => { + const included_in_lib_or_obj = !my_responsibility and + (dep_compile.kind == .lib or dep_compile.kind == .obj or dep_compile.kind == .test_obj); + if (!already_linked and !included_in_lib_or_obj) { + try zig_args.append(gpa, other.getEmittedBin().getPath2(step)); total_linker_objects += 1; + } + }, + .lib => l: { + const other_produces_implib = other.producesImplib(); + const other_is_static = other_produces_implib or other.isStaticLibrary(); - if (other.linkage == .dynamic and - compile.rootModuleTarget().os.tag != .windows) - { - if (Dir.path.dirname(full_path_lib)) |dirname| { - try zig_args.append(gpa, "-rpath"); - try zig_args.append(gpa, dirname); - } + if (compile.isStaticLibrary() and other_is_static) { + // Avoid putting a static library inside a static library. + break :l; + } + + // For DLLs, we must link against the implib. + // For everything else, we directly link + // against the library file. + const full_path_lib = if (other_produces_implib) + try other.getGeneratedFilePath("generated_implib", &compile.step) + else + try other.getGeneratedFilePath("generated_bin", &compile.step); + + try zig_args.append(gpa, full_path_lib); + total_linker_objects += 1; + + if (other.linkage == .dynamic and + compile.rootModuleTarget().os.tag != .windows) + { + if (Dir.path.dirname(full_path_lib)) |dirname| { + try zig_args.append(gpa, "-rpath"); + try zig_args.append(gpa, dirname); } - }, - } - }, - .assembly_file => |asm_file| l: { - if (!my_responsibility) break :l; - - if (prev_has_cflags) { - try zig_args.append(gpa, "-cflags"); - try zig_args.append(gpa, "--"); - prev_has_cflags = false; - } - try zig_args.append(gpa, asm_file.getPath2(mod.owner, step)); - total_linker_objects += 1; - }, - - .c_source_file => |c_source_file| l: { - if (!my_responsibility) break :l; - - if (prev_has_cflags or c_source_file.flags.len != 0) { - try zig_args.append(gpa, "-cflags"); - for (c_source_file.flags) |arg| { - try zig_args.append(gpa, arg); } - try zig_args.append(gpa, "--"); + }, + } + }, + .assembly_file => |asm_file| l: { + if (!my_responsibility) break :l; + + if (prev_has_cflags) { + try zig_args.append(gpa, "-cflags"); + try zig_args.append(gpa, "--"); + prev_has_cflags = false; + } + try zig_args.append(gpa, asm_file.getPath2(mod.owner, step)); + total_linker_objects += 1; + }, + + .c_source_file => |c_source_file| l: { + if (!my_responsibility) break :l; + + if (prev_has_cflags or c_source_file.flags.len != 0) { + try zig_args.append(gpa, "-cflags"); + for (c_source_file.flags) |arg| { + try zig_args.append(gpa, arg); } - prev_has_cflags = (c_source_file.flags.len != 0); + try zig_args.append(gpa, "--"); + } + prev_has_cflags = (c_source_file.flags.len != 0); - if (c_source_file.language) |lang| { - try zig_args.append(gpa, "-x"); - try zig_args.append(gpa, lang.internalIdentifier()); + if (c_source_file.language) |lang| { + try zig_args.append(gpa, "-x"); + try zig_args.append(gpa, lang.internalIdentifier()); + } + + try zig_args.append(gpa, c_source_file.file.getPath2(mod.owner, step)); + + if (c_source_file.language != null) { + try zig_args.append(gpa, "-x"); + try zig_args.append(gpa, "none"); + } + total_linker_objects += 1; + }, + + .c_source_files => |c_source_files| l: { + if (!my_responsibility) break :l; + + if (prev_has_cflags or c_source_files.flags.len != 0) { + try zig_args.append(gpa, "-cflags"); + for (c_source_files.flags) |arg| { + try zig_args.append(gpa, arg); } + try zig_args.append(gpa, "--"); + } + prev_has_cflags = (c_source_files.flags.len != 0); - try zig_args.append(gpa, c_source_file.file.getPath2(mod.owner, step)); + if (c_source_files.language) |lang| { + try zig_args.append(gpa, "-x"); + try zig_args.append(gpa, lang.internalIdentifier()); + } - if (c_source_file.language != null) { - try zig_args.append(gpa, "-x"); - try zig_args.append(gpa, "none"); - } - total_linker_objects += 1; - }, + const root_path = c_source_files.root.getPath2(mod.owner, step); + for (c_source_files.files) |file| { + try zig_args.append(gpa, try Dir.path.join(arena, &.{ root_path, file })); + } - .c_source_files => |c_source_files| l: { - if (!my_responsibility) break :l; + if (c_source_files.language != null) { + try zig_args.append(gpa, "-x"); + try zig_args.append(gpa, "none"); + } - if (prev_has_cflags or c_source_files.flags.len != 0) { - try zig_args.append(gpa, "-cflags"); - for (c_source_files.flags) |arg| { - try zig_args.append(gpa, arg); - } - try zig_args.append(gpa, "--"); - } - prev_has_cflags = (c_source_files.flags.len != 0); + total_linker_objects += c_source_files.files.len; + }, - if (c_source_files.language) |lang| { - try zig_args.append(gpa, "-x"); - try zig_args.append(gpa, lang.internalIdentifier()); - } + .win32_resource_file => |rc_source_file| l: { + if (!my_responsibility) break :l; - const root_path = c_source_files.root.getPath2(mod.owner, step); - for (c_source_files.files) |file| { - try zig_args.append(gpa, try Dir.path.join(arena, &.{ root_path, file })); - } - - if (c_source_files.language != null) { - try zig_args.append(gpa, "-x"); - try zig_args.append(gpa, "none"); - } - - total_linker_objects += c_source_files.files.len; - }, - - .win32_resource_file => |rc_source_file| l: { - if (!my_responsibility) break :l; - - if (rc_source_file.flags.len == 0 and rc_source_file.include_paths.len == 0) { - if (prev_has_rcflags) { - try zig_args.append(gpa, "-rcflags"); - try zig_args.append(gpa, "--"); - prev_has_rcflags = false; - } - } else { + if (rc_source_file.flags.len == 0 and rc_source_file.include_paths.len == 0) { + if (prev_has_rcflags) { try zig_args.append(gpa, "-rcflags"); - for (rc_source_file.flags) |arg| { - try zig_args.append(gpa, arg); - } - for (rc_source_file.include_paths) |include_path| { - try zig_args.append(gpa, "/I"); - try zig_args.append(gpa, include_path.getPath2(mod.owner, step)); - } try zig_args.append(gpa, "--"); - prev_has_rcflags = true; + prev_has_rcflags = false; } - try zig_args.append(gpa, rc_source_file.file.getPath2(mod.owner, step)); - total_linker_objects += 1; - }, - } - } + } else { + try zig_args.append(gpa, "-rcflags"); + for (rc_source_file.flags) |arg| { + try zig_args.append(gpa, arg); + } + for (rc_source_file.include_paths) |include_path| { + try zig_args.append(gpa, "/I"); + try zig_args.append(gpa, include_path.getPath2(mod.owner, step)); + } + try zig_args.append(gpa, "--"); + prev_has_rcflags = true; + } + try zig_args.append(gpa, rc_source_file.file.getPath2(mod.owner, step)); + total_linker_objects += 1; + }, + }; // We need to emit the --mod argument here so that the above link objects // have the correct parent module, but only if the module is part of @@ -448,48 +488,47 @@ fn lowerZigArgs( } if (total_linker_objects == 0) { - return step.fail("the linker needs one or more objects to link", .{}); + return step.fail(maker, "the linker needs one or more objects to link", .{}); } for (frameworks.keys(), frameworks.values()) |name, info| { + try zig_args.ensureUnusedCapacity(gpa, 2); if (info.needed) { - try zig_args.append(gpa, "-needed_framework"); + zig_args.appendAssumeCapacity("-needed_framework"); } else if (info.weak) { - try zig_args.append(gpa, "-weak_framework"); + zig_args.appendAssumeCapacity("-weak_framework"); } else { - try zig_args.append(gpa, "-framework"); + zig_args.appendAssumeCapacity("-framework"); } - try zig_args.append(gpa, name); + zig_args.appendAssumeCapacity(name.slice(conf)); } - if (is_linking_libcpp) { - try zig_args.append(gpa, "-lc++"); - } - - if (is_linking_libc) { - try zig_args.append(gpa, "-lc"); - } + try zig_args.ensureUnusedCapacity(gpa, 2); + if (is_linking_libcpp) zig_args.appendAssumeCapacity("-lc++"); + if (is_linking_libc) zig_args.appendAssumeCapacity("-lc"); } - if (compile.win32_manifest) |manifest_file| { + if (true) @panic("TODO"); + + if (conf_comp.win32_manifest) |manifest_file| { try zig_args.append(gpa, manifest_file.getPath2(step)); } - if (compile.win32_module_definition) |module_file| { + if (conf_comp.win32_module_definition) |module_file| { try zig_args.append(gpa, module_file.getPath2(step)); } - if (compile.image_base) |image_base| { + if (conf_comp.image_base) |image_base| { try zig_args.appendSlice(gpa, &.{ "--image-base", try allocPrint(arena, "0x{x}", .{image_base}), }); } - for (compile.filters) |filter| { + for (conf_comp.filters) |filter| { try zig_args.appendSlice(gpa, &.{ "--test-filter", filter }); } - if (compile.test_runner) |test_runner| { + if (conf_comp.test_runner) |test_runner| { try zig_args.appendSlice(gpa, &.{ "--test-runner", test_runner.path.getPath2(step) }); } @@ -502,8 +541,8 @@ fn lowerZigArgs( try addBool(gpa, zig_args, "--verbose-cimport", graph.verbose_cimport); try addBool(gpa, zig_args, "--verbose-air", graph.verbose_air); try addBool(gpa, zig_args, "--verbose-llvm-ir", graph.verbose_llvm_ir); - try addBool(gpa, zig_args, "--verbose-link", graph.verbose_link or compile.verbose_link); - try addBool(gpa, zig_args, "--verbose-cc", graph.verbose_cc or compile.verbose_cc); + try addBool(gpa, zig_args, "--verbose-link", graph.verbose_link or conf_comp.flags.verbose_link); + try addBool(gpa, zig_args, "--verbose-cc", graph.verbose_cc or conf_comp.flags.verbose_cc); try addBool(gpa, zig_args, "--verbose-llvm-cpu-features", graph.verbose_llvm_cpu_features); try addBool(gpa, zig_args, "--time-report", graph.time_report); @@ -515,49 +554,49 @@ fn lowerZigArgs( if (compile.generated_llvm_ir != null) try zig_args.append(gpa, "-femit-llvm-ir"); if (compile.generated_h != null) try zig_args.append(gpa, "-femit-h"); - try addFlag(gpa, zig_args, "formatted-panics", compile.formatted_panics); + try addFlag(gpa, zig_args, "formatted-panics", conf_comp.flags.formatted_panics); - switch (compile.compress_debug_sections) { + switch (conf_comp.compress_debug_sections) { .none => {}, .zlib => try zig_args.append(gpa, "--compress-debug-sections=zlib"), .zstd => try zig_args.append(gpa, "--compress-debug-sections=zstd"), } - if (compile.link_eh_frame_hdr) { + if (conf_comp.flags.link_eh_frame_hdr) { try zig_args.append(gpa, "--eh-frame-hdr"); } - if (compile.link_emit_relocs) { + if (conf_comp.flags.link_emit_relocs) { try zig_args.append(gpa, "--emit-relocs"); } - if (compile.link_function_sections) { + if (conf_comp.flags.link_function_sections) { try zig_args.append(gpa, "-ffunction-sections"); } - if (compile.link_data_sections) { + if (conf_comp.flags.link_data_sections) { try zig_args.append(gpa, "-fdata-sections"); } - if (compile.link_gc_sections) |x| { + if (conf_comp.flags.link_gc_sections) |x| { try zig_args.append(gpa, if (x) "--gc-sections" else "--no-gc-sections"); } - if (!compile.linker_dynamicbase) { + if (!conf_comp.flags.linker_dynamicbase) { try zig_args.append(gpa, "--no-dynamicbase"); } - if (compile.linker_allow_shlib_undefined) |x| { + if (conf_comp.flags.linker_allow_shlib_undefined) |x| { try zig_args.append(gpa, if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined"); } - if (compile.link_z_notext) try zig_args.appendSlice(gpa, &.{ "-z", "notext" }); - if (!compile.link_z_relro) try zig_args.appendSlice(gpa, &.{ "-z", "norelro" }); - if (compile.link_z_lazy) try zig_args.appendSlice(gpa, &.{ "-z", "lazy" }); - if (compile.link_z_common_page_size) |size| try zig_args.appendSlice(gpa, &.{ + if (conf_comp.flags.link_z_notext) try zig_args.appendSlice(gpa, &.{ "-z", "notext" }); + if (!conf_comp.flags.link_z_relro) try zig_args.appendSlice(gpa, &.{ "-z", "norelro" }); + if (conf_comp.flags.link_z_lazy) try zig_args.appendSlice(gpa, &.{ "-z", "lazy" }); + if (conf_comp.flags.link_z_common_page_size) |size| try zig_args.appendSlice(gpa, &.{ "-z", try allocPrint(arena, "common-page-size={d}", .{size}), }); - if (compile.link_z_max_page_size) |size| try zig_args.appendSlice(gpa, &.{ + if (conf_comp.flags.link_z_max_page_size) |size| try zig_args.appendSlice(gpa, &.{ "-z", try allocPrint(arena, "max-page-size={d}", .{size}), }); - if (compile.link_z_defs) try zig_args.appendSlice(gpa, &.{ "-z", "defs" }); + if (conf_comp.flags.link_z_defs) try zig_args.appendSlice(gpa, &.{ "-z", "defs" }); - if (compile.libc_file) |libc_file| { + if (conf_comp.flags.libc_file) |libc_file| { try zig_args.appendSlice(gpa, &.{ "--libc", libc_file.getPath2(step) }); } else if (graph.libc_file) |libc_file| { try zig_args.appendSlice(gpa, &.{ "--libc", libc_file }); @@ -572,18 +611,16 @@ fn lowerZigArgs( if (graph.debug_compiler_runtime_libs) |mode| try zig_args.append(gpa, try allocPrint(arena, "--debug-rt={t}", .{mode})); - try zig_args.append(gpa, "--name"); - try zig_args.append(gpa, compile.name); + try zig_args.appendSlice(gpa, &.{ "--name", conf_comp.root_name.slice(conf) }); if (compile.linkage) |some| switch (some) { .dynamic => try zig_args.append(gpa, "-dynamic"), .static => try zig_args.append(gpa, "-static"), }; if (compile.kind == .lib and compile.linkage != null and compile.linkage.? == .dynamic) { - if (compile.version) |version| { - try zig_args.append(gpa, "--version"); - try zig_args.append(gpa, try allocPrint(arena, "{f}", .{version})); - } + if (compile.version) |version| try zig_args.appendSlice(gpa, &.{ + "--version", try allocPrint(arena, "{f}", .{version}), + }); if (compile.rootModuleTarget().os.tag.isDarwin()) { const install_name = compile.install_name orelse try allocPrint(arena, "@rpath/{s}{s}{s}", .{ @@ -695,7 +732,7 @@ fn lowerZigArgs( for (graph.search_prefixes.items) |search_prefix| { var prefix_dir = cwd.openDir(io, search_prefix, .{}) catch |err| { - return step.fail("unable to open prefix directory '{s}': {t}", .{ search_prefix, err }); + return step.fail(maker, "unable to open prefix directory '{s}': {t}", .{ search_prefix, err }); }; defer prefix_dir.close(io); @@ -709,7 +746,7 @@ fn lowerZigArgs( }); } else |err| switch (err) { error.FileNotFound => {}, - else => |e| return step.fail("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", .{})) |_| { @@ -718,7 +755,7 @@ fn lowerZigArgs( }); } else |err| switch (err) { error.FileNotFound => {}, - else => |e| return step.fail("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 }), } } @@ -824,13 +861,13 @@ fn lowerZigArgs( var af = graph.cache_root.handle.createFileAtomic(io, args_file, .{ .replace = false, .make_path = true, - }) catch |e| return step.fail("failed creating tmp args file {f}{s}: {t}", .{ + }) catch |e| return step.fail(maker, "failed creating tmp args file {f}{s}: {t}", .{ graph.cache_root, args_file, e, }); defer af.deinit(io); af.file.writeStreamingAll(io, args) catch |e| { - return step.fail("failed writing args data to tmp file {f}{s}: {t}", .{ + return step.fail(maker, "failed writing args data to tmp file {f}{s}: {t}", .{ graph.cache_root, args_file, e, }); }; @@ -841,7 +878,7 @@ fn lowerZigArgs( error.PathAlreadyExists => { // The args file was created by another concurrent build process. }, - else => |other_err| return step.fail("failed linking tmp file {f}{s}: {t}", .{ + else => |other_err| return step.fail(maker, "failed linking tmp file {f}{s}: {t}", .{ graph.cache_root, args_file, other_err, }), }; @@ -898,14 +935,14 @@ pub fn doAtomicSymLinks( const major_only_path = try Dir.path.join(arena, &.{ out_dir, filename_major_only }); const cwd: Io.Dir = .cwd(); cwd.symLinkAtomic(io, out_basename, major_only_path, .{}) catch |err| { - return step.fail("unable to symlink {s} -> {s}: {t}", .{ + return step.fail(maker, "unable to symlink {s} -> {s}: {t}", .{ major_only_path, out_basename, err, }); }; // sym link for libfoo.so to libfoo.so.1 const name_only_path = try Dir.path.join(arena, &.{ out_dir, filename_name_only }); cwd.symLinkAtomic(io, filename_major_only, name_only_path, .{}) catch |err| { - return step.fail("unable to symlink {s} -> {s}: {t}", .{ + return step.fail(maker, "unable to symlink {s} -> {s}: {t}", .{ name_only_path, filename_major_only, err, }); }; @@ -1078,7 +1115,7 @@ fn runPkgConfig(compile: *Compile, maker: *Maker, lib_name: []const u8) !PkgConf } else if (mem.startsWith(u8, arg, wl_rpath_prefix)) { try zig_cflags.appendSlice(&[_][]const u8{ "-rpath", arg[wl_rpath_prefix.len..] }); } else if (b.debug_pkg_config) { - return compile.step.fail("unknown pkg-config flag '{s}'", .{arg}); + return compile.step.fail(maker, "unknown pkg-config flag '{s}'", .{arg}); } } @@ -1088,7 +1125,7 @@ fn runPkgConfig(compile: *Compile, maker: *Maker, lib_name: []const u8) !PkgConf }; } -fn checkCompileErrors(compile: *Compile) !void { +fn checkCompileErrors(compile: *Compile, maker: *Maker) !void { // Clear this field so that it does not get printed by the build runner. const actual_eb = compile.step.result_error_bundle; compile.step.result_error_bundle = .empty; @@ -1115,7 +1152,7 @@ fn checkCompileErrors(compile: *Compile) !void { switch (expect_errors) { .starts_with => |expect_starts_with| { if (std.mem.startsWith(u8, actual_errors, expect_starts_with)) return; - return compile.step.fail( + return compile.step.fail(maker, \\ \\========= should start with: ============ \\{s} @@ -1130,7 +1167,7 @@ fn checkCompileErrors(compile: *Compile) !void { return; } - return compile.step.fail( + return compile.step.fail(maker, \\ \\========= should contain: =============== \\{s} @@ -1153,7 +1190,7 @@ fn checkCompileErrors(compile: *Compile) !void { return; } - return compile.step.fail( + return compile.step.fail(maker, \\ \\========= should contain: =============== \\{s} @@ -1180,7 +1217,7 @@ fn checkCompileErrors(compile: *Compile) !void { if (mem.eql(u8, expected_generated.items, actual_errors)) return; - return compile.step.fail( + return compile.step.fail(maker, \\ \\========= expected: ===================== \\{s} @@ -1217,42 +1254,109 @@ fn moduleNeedsCliArg(mod: *const Module) bool { } const CliNamedModules = struct { - modules: std.AutoArrayHashMapUnmanaged(*Module, void), + modules: std.AutoArrayHashMapUnmanaged(Configuration.Module.Index, void), names: std.StringArrayHashMapUnmanaged(void), /// Traverse the whole dependency graph and give every module a unique /// name, ideally one named after what it's called somewhere in the graph. /// It will help here to have both a mapping from module to name and a set /// of all the currently-used names. - fn init(arena: Allocator, root_module: *Module) Allocator.Error!CliNamedModules { - var compile: CliNamedModules = .{ + fn init( + arena: Allocator, + module_graph: *ModuleGraph, + compile_index: Configuration.Step.Index, + maker: *const Maker, + ) Allocator.Error!CliNamedModules { + const conf = &maker.scanned_config.configuration; + const conf_compile = compile_index.ptr(conf).extended.get(conf.extra).compile; + + var result: CliNamedModules = .{ .modules = .{}, .names = .{}, }; - const graph = root_module.getGraph(); + const modules = try getModuleList(arena, module_graph, conf_compile.root_module, conf); { - assert(graph.modules[0] == root_module); - try compile.modules.put(arena, root_module, {}); - try compile.names.put(arena, "root", {}); + assert(conf_compile.root_module == modules.keys()[0]); + try result.modules.put(arena, conf_compile.root_module, {}); + try result.names.put(arena, "root", {}); } - for (graph.modules[1..], graph.names[1..]) |mod, orig_name| { - var name = orig_name; + for (modules.keys()[1..], modules.values()[1..]) |mod, orig_name| { + const orig_name_slice = orig_name.slice(conf); + var name: []const u8 = orig_name_slice; var n: usize = 0; while (true) { - const gop = try compile.names.getOrPut(arena, name); + const gop = try result.names.getOrPut(arena, name); if (!gop.found_existing) { - try compile.modules.putNoClobber(arena, mod, {}); + try result.modules.putNoClobber(arena, mod, {}); break; } - name = try allocPrint(arena, "{s}{d}", .{ orig_name, n }); + name = try allocPrint(arena, "{s}{d}", .{ orig_name_slice, n }); n += 1; } } - return compile; + return result; } }; -fn getCompileDependencies(chase_dynamic: bool) void { - _ = chase_dynamic; - @panic("TODO"); +fn getCompileDependencies( + arena: Allocator, + module_graph: *ModuleGraph, + conf: *const Configuration, + start: Configuration.Step.Index, + chase_dynamic: bool, +) ![]const Configuration.Step.Index { + var compiles: std.AutoArrayHashMapUnmanaged(Configuration.Step.Index, void) = .empty; + var compiles_i: usize = 0; + + try compiles.putNoClobber(arena, start, {}); + + while (compiles_i < compiles.count()) : (compiles_i += 1) { + const step = compiles.keys()[compiles_i].ptr(conf); + const compile = step.extended.get(conf.extra).compile; + const modules = try getModuleList(arena, module_graph, compile.root_module, conf); + + for (modules.keys()) |mod_index| { + const mod = mod_index.get(conf); + for (0..mod.link_objects.len) |i| { + switch (mod.link_objects.get(conf.extra, i)) { + .other_step => |other_compile_index| { + const other_compile = other_compile_index.ptr(conf).extended.get(conf.extra).compile; + if (!chase_dynamic and other_compile.isDynamicLibrary()) continue; + try compiles.put(arena, other_compile_index, {}); + }, + else => {}, + } + } + } + } + + return compiles.keys(); +} + +/// Returned pointer expires upon next call to `getModuleList`. +fn getModuleList( + arena: Allocator, + module_graph: *ModuleGraph, + root_module: Configuration.Module.Index, + conf: *const Configuration, +) !*ModuleList { + const gop = try module_graph.getOrPutAdapted(arena, root_module, @as(ModuleListContext.Adapter, .{})); + const modules = gop.key_ptr; + + if (gop.found_existing) return modules; + modules.* = .empty; + try modules.putNoClobber(arena, root_module, .root); + + var i: usize = 0; + + while (i < modules.entries.len) : (i += 1) { + const dep_index = modules.keys()[i]; + const dep = dep_index.get(conf); + const imports = dep.import_table.get(conf).imports; + try modules.ensureUnusedCapacity(arena, imports.mal.len); + for (imports.mal.items(.name), imports.mal.items(.module)) |import_name, other_mod| + modules.putAssumeCapacity(other_mod, import_name); + } + + return modules; } diff --git a/lib/compiler/configurer.zig b/lib/compiler/configurer.zig index d1178901ef..bea788fba1 100644 --- a/lib/compiler/configurer.zig +++ b/lib/compiler/configurer.zig @@ -202,6 +202,7 @@ pub fn main(init: process.Init.Minimal) !void { var wc: Configuration.Wip = .init(gpa); defer wc.deinit(); assert(try wc.addString("") == .empty); + assert(try wc.addString("root") == .root); try serializeSystemIntegrationOptions(&graph, &wc); diff --git a/lib/std/Build/Module.zig b/lib/std/Build/Module.zig index bc3393a17a..1f8dcb8d38 100644 --- a/lib/std/Build/Module.zig +++ b/lib/std/Build/Module.zig @@ -668,11 +668,9 @@ pub const Graph = struct { names: []const []const u8, }; -/// Intended to be used during the make phase only. -/// -/// Given that `root` is the root `Module` of a compilation, return all `Module`s -/// in the module graph, including `root` itself. `root` is guaranteed to be the -/// first module in the returned slice. +/// Given that `root` is the root `Module` of a compilation, return all +/// `Module` in the module graph, including `root` itself. `root` is guaranteed +/// to be the first module in the returned slice. pub fn getGraph(root: *Module) Graph { if (root.cached_graph.modules.len != 0) { return root.cached_graph; diff --git a/lib/std/zig/Configuration.zig b/lib/std/zig/Configuration.zig index f739dab11c..23d027e777 100644 --- a/lib/std/zig/Configuration.zig +++ b/lib/std/zig/Configuration.zig @@ -859,6 +859,10 @@ pub const Step = extern struct { version_script: bool, _: u18 = 0, }; + + pub fn isDynamicLibrary(compile: *const Compile) bool { + return compile.flags3.kind == .lib and compile.flags2.linkage == .dynamic; + } }; pub const CheckFile = struct { @@ -1243,6 +1247,13 @@ pub const ImportTable = struct { pub const Index = enum(u32) { invalid = maxInt(u32), _, + + pub fn get(this: @This(), c: *const Configuration) ImportTable { + return switch (this) { + .invalid => unreachable, + _ => extraData(c, ImportTable, @intFromEnum(this)), + }; + } }; }; @@ -1313,6 +1324,8 @@ pub const InstallDestDir = enum(u32) { /// Points into `string_bytes`, null-terminated. pub const OptionalString = enum(u32) { empty = 0, + /// The string "root". + root = 1, none = maxInt(u32), _, @@ -1326,6 +1339,8 @@ pub const OptionalString = enum(u32) { /// Points into `string_bytes`, null-terminated. pub const String = enum(u32) { empty = 0, + /// The string "root". + root = 1, _, pub fn slice(index: String, c: *const Configuration) [:0]const u8 { @@ -1954,15 +1969,23 @@ pub const Storage = enum { }; /// Valid to call only when serializing. - pub fn init(slice: []const Union) @This() { - return .{ .data = slice.ptr, .len = slice.len }; + pub fn init(s: []const Union) @This() { + return .{ .data = s.ptr, .len = s.len }; } /// Valid to call only when deserializing. - pub fn get(this: *const @This(), extra: []const u32) []const u32 { + pub fn slice(this: *const @This(), extra: []const u32) []const u32 { return extra[@intFromPtr(this.data)..][0..this.len]; } + /// Valid to call only when deserializing. + pub fn get(this: *const @This(), extra: []const u32, i: usize) Union { + const elem = slice(this, extra)[i]; + return switch (this.tag(extra, i)) { + inline else => |comptime_tag| @unionInit(Union, @tagName(comptime_tag), @enumFromInt(elem)), + }; + } + /// Valid to call only when deserializing. pub fn tag(this: *const @This(), extra: []const u32, i: usize) Tag { _ = this; @@ -2093,7 +2116,7 @@ pub const Storage = enum { const len = buffer[data_start - 1]; defer i.* = data_start + len * @typeInfo(Field.Elem).@"struct".fields.len; return .{ .mal = .{ - .bytes = @ptrCast(buffer[data_start..][0..len]), + .bytes = @ptrCast(@constCast(buffer[data_start..][0..len])), .len = len, .capacity = len, } };