Commit 142471fcc4

Andrew Kelley <andrew@ziglang.org>
2023-12-03 05:51:34
zig build system: change target, compilation, and module APIs
Introduce the concept of "target query" and "resolved target". A target query is what the user specifies, with some things left to default. A resolved target has the default things discovered and populated. In the future, std.zig.CrossTarget will be rename to std.Target.Query. Introduces `std.Build.resolveTargetQuery` to get from one to the other. The concept of `main_mod_path` is gone, no longer supported. You have to put the root source file at the module root now. * remove deprecated API * update build.zig for the breaking API changes in this branch * move std.Build.Step.Compile.BuildId to std.zig.BuildId * add more options to std.Build.ExecutableOptions, std.Build.ObjectOptions, std.Build.SharedLibraryOptions, std.Build.StaticLibraryOptions, and std.Build.TestOptions. * remove `std.Build.constructCMacro`. There is no use for this API. * deprecate `std.Build.Step.Compile.defineCMacro`. Instead, `std.Build.Module.addCMacro` is provided. - remove `std.Build.Step.Compile.defineCMacroRaw`. * deprecate `std.Build.Step.Compile.linkFrameworkNeeded` - use `std.Build.Module.linkFramework` * deprecate `std.Build.Step.Compile.linkFrameworkWeak` - use `std.Build.Module.linkFramework` * move more logic into `std.Build.Module` * allow `target` and `optimize` to be `null` when creating a Module. Along with other fields, those unspecified options will be inherited from parent `Module` when inserted into an import table. * the `target` field of `addExecutable` is now required. pass `b.host` to get the host target.
1 parent 579f572
Changed files (122)
deps
aro
lib
src
test
behavior
link
bss
common_symbols
common_symbols_alignment
glibc_compat
interdependent_static_c_libs
macho
bugs
13056
13457
16308
16628
dead_strip
dead_strip_dylibs
dylib
empty
entry
entry_in_archive
entry_in_dylib
headerpad
linksection
needed_framework
needed_library
objc
objcpp
pagezero
reexports
search_strategy
stack_size
strict_validation
tbdv3
tls
unwind_info
weak_framework
weak_library
static_libs_from_object_files
wasm
archive
basic-features
bss
export
export-data
extern
extern-mangle
function-table
infer-features
producers
segments
shared-memory
stack_pointer
type
src
standalone
c_compiler
child_process
compiler_rt_panic
dep_diamond
dep_mutually_recursive
dep_recursive
dep_shared_builtin
dep_triangle
embed_generated_file
empty_env
extern
global_linkage
install_raw_hex
ios
issue_11595
issue_12588
issue_12706
issue_339
issue_8550
load_dynamic_library
main_pkg_path
mix_c_files
mix_o_files
options
pie
pkg_import
self_exe_symlink
shared_library
stack_iterator
static_c_lib
strip_empty_loop
strip_struct_init
test_runner_module_imports
windows_resources
windows_spawn
zerolength_check
deps/aro/build/GenerateDef.zig
@@ -21,7 +21,7 @@ pub const Options = struct {
     pub const Kind = enum { dafsa, named };
 };
 
-pub fn create(owner: *std.Build, options: Options) std.Build.ModuleDependency {
+pub fn create(owner: *std.Build, options: Options) std.Build.Module.Import {
     const self = owner.allocator.create(GenerateDef) catch @panic("OOM");
     const path = owner.pathJoin(&.{ options.src_prefix, options.name });
 
@@ -39,7 +39,7 @@ pub fn create(owner: *std.Build, options: Options) std.Build.ModuleDependency {
         .generated_file = .{ .step = &self.step },
     };
     const module = self.step.owner.createModule(.{
-        .source_file = .{ .generated = &self.generated_file },
+        .root_source_file = .{ .generated = &self.generated_file },
     });
     return .{
         .module = module,
lib/std/Build/Step/CheckObject.zig
@@ -44,11 +44,11 @@ pub fn create(
 
 const SearchPhrase = struct {
     string: []const u8,
-    file_source: ?std.Build.LazyPath = null,
+    lazy_path: ?std.Build.LazyPath = null,
 
     fn resolve(phrase: SearchPhrase, b: *std.Build, step: *Step) []const u8 {
-        const file_source = phrase.file_source orelse return phrase.string;
-        return b.fmt("{s} {s}", .{ phrase.string, file_source.getPath2(b, step) });
+        const lazy_path = phrase.lazy_path orelse return phrase.string;
+        return b.fmt("{s} {s}", .{ phrase.string, lazy_path.getPath2(b, step) });
     }
 };
 
@@ -321,14 +321,14 @@ pub fn checkExact(self: *CheckObject, phrase: []const u8) void {
 
 /// Like `checkExact()` but takes an additional argument `LazyPath` which will be
 /// resolved to a full search query in `make()`.
-pub fn checkExactPath(self: *CheckObject, phrase: []const u8, file_source: std.Build.LazyPath) void {
-    self.checkExactInner(phrase, file_source);
+pub fn checkExactPath(self: *CheckObject, phrase: []const u8, lazy_path: std.Build.LazyPath) void {
+    self.checkExactInner(phrase, lazy_path);
 }
 
-fn checkExactInner(self: *CheckObject, phrase: []const u8, file_source: ?std.Build.LazyPath) void {
+fn checkExactInner(self: *CheckObject, phrase: []const u8, lazy_path: ?std.Build.LazyPath) void {
     assert(self.checks.items.len > 0);
     const last = &self.checks.items[self.checks.items.len - 1];
-    last.exact(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source });
+    last.exact(.{ .string = self.step.owner.dupe(phrase), .lazy_path = lazy_path });
 }
 
 /// Adds a fuzzy match phrase to the latest created Check.
@@ -336,16 +336,20 @@ pub fn checkContains(self: *CheckObject, phrase: []const u8) void {
     self.checkContainsInner(phrase, null);
 }
 
-/// Like `checkContains()` but takes an additional argument `FileSource` which will be
+/// Like `checkContains()` but takes an additional argument `lazy_path` which will be
 /// resolved to a full search query in `make()`.
-pub fn checkContainsPath(self: *CheckObject, phrase: []const u8, file_source: std.Build.LazyPath) void {
-    self.checkContainsInner(phrase, file_source);
+pub fn checkContainsPath(
+    self: *CheckObject,
+    phrase: []const u8,
+    lazy_path: std.Build.LazyPath,
+) void {
+    self.checkContainsInner(phrase, lazy_path);
 }
 
-fn checkContainsInner(self: *CheckObject, phrase: []const u8, file_source: ?std.Build.FileSource) void {
+fn checkContainsInner(self: *CheckObject, phrase: []const u8, lazy_path: ?std.Build.LazyPath) void {
     assert(self.checks.items.len > 0);
     const last = &self.checks.items[self.checks.items.len - 1];
-    last.contains(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source });
+    last.contains(.{ .string = self.step.owner.dupe(phrase), .lazy_path = lazy_path });
 }
 
 /// Adds an exact match phrase with variable extractor to the latest created Check.
@@ -353,16 +357,16 @@ pub fn checkExtract(self: *CheckObject, phrase: []const u8) void {
     self.checkExtractInner(phrase, null);
 }
 
-/// Like `checkExtract()` but takes an additional argument `FileSource` which will be
+/// Like `checkExtract()` but takes an additional argument `LazyPath` which will be
 /// resolved to a full search query in `make()`.
-pub fn checkExtractFileSource(self: *CheckObject, phrase: []const u8, file_source: std.Build.FileSource) void {
-    self.checkExtractInner(phrase, file_source);
+pub fn checkExtractLazyPath(self: *CheckObject, phrase: []const u8, lazy_path: std.Build.LazyPath) void {
+    self.checkExtractInner(phrase, lazy_path);
 }
 
-fn checkExtractInner(self: *CheckObject, phrase: []const u8, file_source: ?std.Build.FileSource) void {
+fn checkExtractInner(self: *CheckObject, phrase: []const u8, lazy_path: ?std.Build.LazyPath) void {
     assert(self.checks.items.len > 0);
     const last = &self.checks.items[self.checks.items.len - 1];
-    last.extract(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source });
+    last.extract(.{ .string = self.step.owner.dupe(phrase), .lazy_path = lazy_path });
 }
 
 /// Adds another searched phrase to the latest created Check
@@ -371,16 +375,16 @@ pub fn checkNotPresent(self: *CheckObject, phrase: []const u8) void {
     self.checkNotPresentInner(phrase, null);
 }
 
-/// Like `checkExtract()` but takes an additional argument `FileSource` which will be
+/// Like `checkExtract()` but takes an additional argument `LazyPath` which will be
 /// resolved to a full search query in `make()`.
-pub fn checkNotPresentFileSource(self: *CheckObject, phrase: []const u8, file_source: std.Build.FileSource) void {
-    self.checkNotPresentInner(phrase, file_source);
+pub fn checkNotPresentLazyPath(self: *CheckObject, phrase: []const u8, lazy_path: std.Build.LazyPath) void {
+    self.checkNotPresentInner(phrase, lazy_path);
 }
 
-fn checkNotPresentInner(self: *CheckObject, phrase: []const u8, file_source: ?std.Build.FileSource) void {
+fn checkNotPresentInner(self: *CheckObject, phrase: []const u8, lazy_path: ?std.Build.LazyPath) void {
     assert(self.checks.items.len > 0);
     const last = &self.checks.items[self.checks.items.len - 1];
-    last.notPresent(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source });
+    last.notPresent(.{ .string = self.step.owner.dupe(phrase), .lazy_path = lazy_path });
 }
 
 /// Creates a new check checking in the file headers (section, program headers, etc.).
lib/std/Build/Step/Compile.zig
@@ -88,7 +88,7 @@ each_lib_rpath: ?bool = null,
 /// As an example, the bloaty project refuses to work unless its inputs have
 /// build ids, in order to prevent accidental mismatches.
 /// The default is to not include this section because it slows down linking.
-build_id: ?BuildId = null,
+build_id: ?std.zig.BuildId = null,
 
 /// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
 /// file.
@@ -200,7 +200,7 @@ pub const ExpectedCompileErrors = union(enum) {
     exact: []const []const u8,
 };
 
-const Entry = union(enum) {
+pub const Entry = union(enum) {
     /// Let the compiler decide whether to make an entry point and what to name
     /// it.
     default,
@@ -232,82 +232,6 @@ pub const Options = struct {
     win32_manifest: ?LazyPath = null,
 };
 
-pub const BuildId = union(enum) {
-    none,
-    fast,
-    uuid,
-    sha1,
-    md5,
-    hexstring: HexString,
-
-    pub fn eql(a: BuildId, b: BuildId) bool {
-        const a_tag = std.meta.activeTag(a);
-        const b_tag = std.meta.activeTag(b);
-        if (a_tag != b_tag) return false;
-        return switch (a) {
-            .none, .fast, .uuid, .sha1, .md5 => true,
-            .hexstring => |a_hexstring| mem.eql(u8, a_hexstring.toSlice(), b.hexstring.toSlice()),
-        };
-    }
-
-    pub const HexString = struct {
-        bytes: [32]u8,
-        len: u8,
-
-        /// Result is byte values, *not* hex-encoded.
-        pub fn toSlice(hs: *const HexString) []const u8 {
-            return hs.bytes[0..hs.len];
-        }
-    };
-
-    /// Input is byte values, *not* hex-encoded.
-    /// Asserts `bytes` fits inside `HexString`
-    pub fn initHexString(bytes: []const u8) BuildId {
-        var result: BuildId = .{ .hexstring = .{
-            .bytes = undefined,
-            .len = @as(u8, @intCast(bytes.len)),
-        } };
-        @memcpy(result.hexstring.bytes[0..bytes.len], bytes);
-        return result;
-    }
-
-    /// Converts UTF-8 text to a `BuildId`.
-    pub fn parse(text: []const u8) !BuildId {
-        if (mem.eql(u8, text, "none")) {
-            return .none;
-        } else if (mem.eql(u8, text, "fast")) {
-            return .fast;
-        } else if (mem.eql(u8, text, "uuid")) {
-            return .uuid;
-        } else if (mem.eql(u8, text, "sha1") or mem.eql(u8, text, "tree")) {
-            return .sha1;
-        } else if (mem.eql(u8, text, "md5")) {
-            return .md5;
-        } else if (mem.startsWith(u8, text, "0x")) {
-            var result: BuildId = .{ .hexstring = undefined };
-            const slice = try std.fmt.hexToBytes(&result.hexstring.bytes, text[2..]);
-            result.hexstring.len = @as(u8, @intCast(slice.len));
-            return result;
-        }
-        return error.InvalidBuildIdStyle;
-    }
-
-    test parse {
-        try std.testing.expectEqual(BuildId.md5, try parse("md5"));
-        try std.testing.expectEqual(BuildId.none, try parse("none"));
-        try std.testing.expectEqual(BuildId.fast, try parse("fast"));
-        try std.testing.expectEqual(BuildId.uuid, try parse("uuid"));
-        try std.testing.expectEqual(BuildId.sha1, try parse("sha1"));
-        try std.testing.expectEqual(BuildId.sha1, try parse("tree"));
-
-        try std.testing.expect(BuildId.initHexString("").eql(try parse("0x")));
-        try std.testing.expect(BuildId.initHexString("\x12\x34\x56").eql(try parse("0x123456")));
-        try std.testing.expectError(error.InvalidLength, parse("0x12-34"));
-        try std.testing.expectError(error.InvalidCharacter, parse("0xfoobbb"));
-        try std.testing.expectError(error.InvalidBuildIdStyle, parse("yaddaxxx"));
-    }
-};
-
 pub const Kind = enum {
     exe,
     lib,
@@ -329,6 +253,8 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
     else
         owner.fmt("{s} ", .{name});
 
+    const target = options.root_module.target.?.target;
+
     const step_name = owner.fmt("{s} {s}{s} {s}", .{
         switch (options.kind) {
             .exe => "zig build-exe",
@@ -337,16 +263,13 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
             .@"test" => "zig test",
         },
         name_adjusted,
-        @tagName(options.root_module.optimize),
-        options.root_module.target.zigTriple(owner.allocator) catch @panic("OOM"),
+        @tagName(options.root_module.optimize orelse .Debug),
+        target.zigTriple(owner.allocator) catch @panic("OOM"),
     });
 
-    const target_info = NativeTargetInfo.detect(options.root_module.target) catch
-        @panic("unhandled error");
-
     const out_filename = std.zig.binNameAlloc(owner.allocator, .{
         .root_name = name,
-        .target = target_info.target,
+        .target = target,
         .output_mode = switch (options.kind) {
             .lib => .Lib,
             .obj => .Obj,
@@ -361,7 +284,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
 
     const self = owner.allocator.create(Compile) catch @panic("OOM");
     self.* = .{
-        .root_module = Module.init(owner, options.root_module, self),
+        .root_module = undefined,
         .verbose_link = false,
         .verbose_cc = false,
         .linkage = options.linkage,
@@ -403,6 +326,8 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
         .use_lld = options.use_lld,
     };
 
+    self.root_module.init(owner, options.root_module, self);
+
     if (options.zig_lib_dir) |lp| {
         self.zig_lib_dir = lp.dupe(self.step.owner);
         lp.addStepDependencies(&self.step);
@@ -410,7 +335,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
 
     // Only the PE/COFF format has a Resource Table which is where the manifest
     // gets embedded, so for any other target the manifest file is just ignored.
-    if (target_info.target.ofmt == .coff) {
+    if (target.ofmt == .coff) {
         if (options.win32_manifest) |lp| {
             self.win32_manifest = lp.dupe(self.step.owner);
             lp.addStepDependencies(&self.step);
@@ -421,14 +346,14 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
         if (self.linkage != null and self.linkage.? == .static) {
             self.out_lib_filename = self.out_filename;
         } else if (self.version) |version| {
-            if (target_info.target.isDarwin()) {
+            if (target.isDarwin()) {
                 self.major_only_filename = owner.fmt("lib{s}.{d}.dylib", .{
                     self.name,
                     version.major,
                 });
                 self.name_only_filename = owner.fmt("lib{s}.dylib", .{self.name});
                 self.out_lib_filename = self.out_filename;
-            } else if (target_info.target.os.tag == .windows) {
+            } else if (target.os.tag == .windows) {
                 self.out_lib_filename = owner.fmt("{s}.lib", .{self.name});
             } else {
                 self.major_only_filename = owner.fmt("lib{s}.so.{d}", .{ self.name, version.major });
@@ -436,9 +361,9 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
                 self.out_lib_filename = self.out_filename;
             }
         } else {
-            if (target_info.target.isDarwin()) {
+            if (target.isDarwin()) {
                 self.out_lib_filename = self.out_filename;
-            } else if (target_info.target.os.tag == .windows) {
+            } else if (target.os.tag == .windows) {
                 self.out_lib_filename = owner.fmt("{s}.lib", .{self.name});
             } else {
                 self.out_lib_filename = self.out_filename;
@@ -545,7 +470,7 @@ pub const run = @compileError("deprecated; use std.Build.addRunArtifact");
 pub const install = @compileError("deprecated; use std.Build.installArtifact");
 
 pub fn checkObject(self: *Compile) *Step.CheckObject {
-    return Step.CheckObject.create(self.step.owner, self.getEmittedBin(), self.target_info.target.ofmt);
+    return Step.CheckObject.create(self.step.owner, self.getEmittedBin(), self.rootModuleTarget().ofmt);
 }
 
 /// deprecated: use `setLinkerScript`
@@ -562,25 +487,6 @@ pub fn forceUndefinedSymbol(self: *Compile, symbol_name: []const u8) void {
     self.force_undefined_symbols.put(b.dupe(symbol_name), {}) catch @panic("OOM");
 }
 
-pub fn linkFramework(self: *Compile, framework_name: []const u8) void {
-    const b = self.step.owner;
-    self.frameworks.put(b.dupe(framework_name), .{}) catch @panic("OOM");
-}
-
-pub fn linkFrameworkNeeded(self: *Compile, framework_name: []const u8) void {
-    const b = self.step.owner;
-    self.frameworks.put(b.dupe(framework_name), .{
-        .needed = true,
-    }) catch @panic("OOM");
-}
-
-pub fn linkFrameworkWeak(self: *Compile, framework_name: []const u8) void {
-    const b = self.step.owner;
-    self.frameworks.put(b.dupe(framework_name), .{
-        .weak = true,
-    }) catch @panic("OOM");
-}
-
 /// Returns whether the library, executable, or object depends on a particular system library.
 pub fn dependsOnSystemLibrary(self: *const Compile, name: []const u8) bool {
     var is_linking_libc = false;
@@ -598,22 +504,17 @@ pub fn dependsOnSystemLibrary(self: *const Compile, name: []const u8) bool {
         is_linking_libcpp = is_linking_libcpp or module.link_libcpp == true;
     }
 
-    if (self.root_module.target_info.target.is_libc_lib_name(name)) {
+    if (self.rootModuleTarget().is_libc_lib_name(name)) {
         return is_linking_libc;
     }
 
-    if (self.root_module.target_info.target.is_libcpp_lib_name(name)) {
+    if (self.rootModuleTarget().is_libcpp_lib_name(name)) {
         return is_linking_libcpp;
     }
 
     return false;
 }
 
-pub fn linkLibrary(self: *Compile, lib: *Compile) void {
-    assert(lib.kind == .lib);
-    self.linkLibraryOrObject(lib);
-}
-
 pub fn isDynamicLibrary(self: *const Compile) bool {
     return self.kind == .lib and self.linkage == Linkage.dynamic;
 }
@@ -623,16 +524,24 @@ pub fn isStaticLibrary(self: *const Compile) bool {
 }
 
 pub fn producesPdbFile(self: *Compile) bool {
+    const target = self.rootModuleTarget();
     // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
     // TODO: just share this logic with the compiler, silly!
-    if (!self.target.isWindows() and !self.target.isUefi()) return false;
-    if (self.target.getObjectFormat() == .c) return false;
-    if (self.strip == true or (self.strip == null and self.optimize == .ReleaseSmall)) return false;
+    switch (target.os.tag) {
+        .windows, .uefi => {},
+        else => return false,
+    }
+    if (target.ofmt == .c) return false;
+    if (self.root_module.strip == true or
+        (self.root_module.strip == null and self.root_module.optimize == .ReleaseSmall))
+    {
+        return false;
+    }
     return self.isDynamicLibrary() or self.kind == .exe or self.kind == .@"test";
 }
 
 pub fn producesImplib(self: *Compile) bool {
-    return self.isDynamicLibrary() and self.root_module.target_info.target.os.tag == .windows;
+    return self.isDynamicLibrary() and self.rootModuleTarget().os.tag == .windows;
 }
 
 pub fn linkLibC(self: *Compile) void {
@@ -643,18 +552,9 @@ pub fn linkLibCpp(self: *Compile) void {
     self.root_module.link_libcpp = true;
 }
 
-/// If the value is omitted, it is set to 1.
-/// `name` and `value` need not live longer than the function call.
-pub fn defineCMacro(self: *Compile, name: []const u8, value: ?[]const u8) void {
-    const b = self.step.owner;
-    const macro = std.Build.constructCMacro(b.allocator, name, value);
-    self.c_macros.append(macro) catch @panic("OOM");
-}
-
-/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
-pub fn defineCMacroRaw(self: *Compile, name_and_value: []const u8) void {
-    const b = self.step.owner;
-    self.c_macros.append(b.dupe(name_and_value)) catch @panic("OOM");
+/// Deprecated. Use `c.root_module.addCMacro`.
+pub fn defineCMacro(c: *Compile, name: []const u8, value: ?[]const u8) void {
+    c.root_module.addCMacro(name, value orelse "1");
 }
 
 /// Run pkg-config for the given library name and parse the output, returning the arguments
@@ -765,6 +665,20 @@ pub fn linkSystemLibrary2(
     return self.root_module.linkSystemLibrary(name, options);
 }
 
+pub fn linkFramework(c: *Compile, name: []const u8) void {
+    c.root_module.linkFramework(name, .{});
+}
+
+/// Deprecated. Use `c.root_module.linkFramework`.
+pub fn linkFrameworkNeeded(c: *Compile, name: []const u8) void {
+    c.root_module.linkFramework(name, .{ .needed = true });
+}
+
+/// Deprecated. Use `c.root_module.linkFramework`.
+pub fn linkFrameworkWeak(c: *Compile, name: []const u8) void {
+    c.root_module.linkFramework(name, .{ .weak = true });
+}
+
 /// Handy when you have many C/C++ source files and want them all to have the same flags.
 pub fn addCSourceFiles(self: *Compile, options: Module.AddCSourceFilesOptions) void {
     self.root_module.addCSourceFiles(options);
@@ -805,27 +719,18 @@ fn getEmittedFileGeneric(self: *Compile, output_file: *?*GeneratedFile) LazyPath
     return .{ .generated = generated_file };
 }
 
-/// deprecated: use `getEmittedBinDirectory`
-pub const getOutputDirectorySource = getEmittedBinDirectory;
-
 /// Returns the path to the directory that contains the emitted binary file.
 pub fn getEmittedBinDirectory(self: *Compile) LazyPath {
     _ = self.getEmittedBin();
     return self.getEmittedFileGeneric(&self.emit_directory);
 }
 
-/// deprecated: use `getEmittedBin`
-pub const getOutputSource = getEmittedBin;
-
 /// Returns the path to the generated executable, library or object file.
 /// To run an executable built with zig build, use `run`, or create an install step and invoke it.
 pub fn getEmittedBin(self: *Compile) LazyPath {
     return self.getEmittedFileGeneric(&self.generated_bin);
 }
 
-/// deprecated: use `getEmittedImplib`
-pub const getOutputLibSource = getEmittedImplib;
-
 /// Returns the path to the generated import library.
 /// This function can only be called for libraries.
 pub fn getEmittedImplib(self: *Compile) LazyPath {
@@ -833,9 +738,6 @@ pub fn getEmittedImplib(self: *Compile) LazyPath {
     return self.getEmittedFileGeneric(&self.generated_implib);
 }
 
-/// deprecated: use `getEmittedH`
-pub const getOutputHSource = getEmittedH;
-
 /// Returns the path to the generated header file.
 /// This function can only be called for libraries or objects.
 pub fn getEmittedH(self: *Compile) LazyPath {
@@ -843,9 +745,6 @@ pub fn getEmittedH(self: *Compile) LazyPath {
     return self.getEmittedFileGeneric(&self.generated_h);
 }
 
-/// deprecated: use `getEmittedPdb`.
-pub const getOutputPdbSource = getEmittedPdb;
-
 /// Returns the generated PDB file.
 /// If the compilation does not produce a PDB file, this causes a FileNotFound error
 /// at build time.
@@ -882,56 +781,44 @@ pub fn addObjectFile(self: *Compile, source: LazyPath) void {
     self.root_module.addObjectFile(source);
 }
 
-pub fn addObject(self: *Compile, obj: *Compile) void {
-    assert(obj.kind == .obj);
-    self.linkLibraryOrObject(obj);
+pub fn addObject(self: *Compile, object: *Compile) void {
+    self.root_module.addObject(object);
 }
 
-pub fn addAfterIncludePath(self: *Compile, path: LazyPath) void {
-    const b = self.step.owner;
-    self.include_dirs.append(.{ .path_after = path.dupe(b) }) catch @panic("OOM");
-    path.addStepDependencies(&self.step);
+pub fn linkLibrary(self: *Compile, library: *Compile) void {
+    self.root_module.linkLibrary(library);
 }
 
-pub fn addSystemIncludePath(self: *Compile, path: LazyPath) void {
-    const b = self.step.owner;
-    self.include_dirs.append(.{ .path_system = path.dupe(b) }) catch @panic("OOM");
-    path.addStepDependencies(&self.step);
+pub fn addAfterIncludePath(self: *Compile, lazy_path: LazyPath) void {
+    self.root_module.addAfterIncludePath(lazy_path);
 }
 
-pub fn addIncludePath(self: *Compile, path: LazyPath) void {
-    const b = self.step.owner;
-    self.include_dirs.append(.{ .path = path.dupe(b) }) catch @panic("OOM");
-    path.addStepDependencies(&self.step);
+pub fn addSystemIncludePath(self: *Compile, lazy_path: LazyPath) void {
+    self.root_module.addSystemIncludePath(lazy_path);
+}
+
+pub fn addIncludePath(self: *Compile, lazy_path: LazyPath) void {
+    self.root_module.addIncludePath(lazy_path);
 }
 
 pub fn addConfigHeader(self: *Compile, config_header: *Step.ConfigHeader) void {
-    self.step.dependOn(&config_header.step);
-    self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM");
+    self.root_module.addConfigHeader(config_header);
 }
 
-pub fn addLibraryPath(self: *Compile, directory_source: LazyPath) void {
-    const b = self.step.owner;
-    self.lib_paths.append(directory_source.dupe(b)) catch @panic("OOM");
-    directory_source.addStepDependencies(&self.step);
+pub fn addLibraryPath(self: *Compile, directory_path: LazyPath) void {
+    self.root_module.addLibraryPath(directory_path);
 }
 
-pub fn addRPath(self: *Compile, directory_source: LazyPath) void {
-    const b = self.step.owner;
-    self.rpaths.append(directory_source.dupe(b)) catch @panic("OOM");
-    directory_source.addStepDependencies(&self.step);
+pub fn addRPath(self: *Compile, directory_path: LazyPath) void {
+    self.root_module.addRPath(directory_path);
 }
 
-pub fn addSystemFrameworkPath(self: *Compile, directory_source: LazyPath) void {
-    const b = self.step.owner;
-    self.include_dirs.append(.{ .framework_path_system = directory_source.dupe(b) }) catch @panic("OOM");
-    directory_source.addStepDependencies(&self.step);
+pub fn addSystemFrameworkPath(self: *Compile, directory_path: LazyPath) void {
+    self.root_module.addSystemFrameworkPath(directory_path);
 }
 
-pub fn addFrameworkPath(self: *Compile, directory_source: LazyPath) void {
-    const b = self.step.owner;
-    self.include_dirs.append(.{ .framework_path = directory_source.dupe(b) }) catch @panic("OOM");
-    directory_source.addStepDependencies(&self.step);
+pub fn addFrameworkPath(self: *Compile, directory_path: LazyPath) void {
+    self.root_module.addFrameworkPath(directory_path);
 }
 
 pub fn setExecCmd(self: *Compile, args: []const ?[]const u8) void {
@@ -944,20 +831,6 @@ pub fn setExecCmd(self: *Compile, args: []const ?[]const u8) void {
     self.exec_cmd_args = duped_args;
 }
 
-fn linkLibraryOrObject(self: *Compile, other: *Compile) void {
-    other.getEmittedBin().addStepDependencies(&self.step);
-    if (other.target.isWindows() and other.isDynamicLibrary()) {
-        other.getEmittedImplib().addStepDependencies(&self.step);
-    }
-
-    self.link_objects.append(.{ .other_step = other }) catch @panic("OOM");
-    self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM");
-
-    for (other.installed_headers.items) |install_step| {
-        self.step.dependOn(install_step);
-    }
-}
-
 fn appendModuleArgs(cs: *Compile, zig_args: *ArrayList([]const u8)) !void {
     const b = cs.step.owner;
     // First, traverse the whole dependency graph and give every module a
@@ -1014,7 +887,7 @@ fn appendModuleArgs(cs: *Compile, zig_args: *ArrayList([]const u8)) !void {
 fn constructDepString(
     allocator: std.mem.Allocator,
     mod_names: std.AutoArrayHashMapUnmanaged(*Module, []const u8),
-    deps: std.StringArrayHashMap(*Module),
+    deps: std.StringArrayHashMapUnmanaged(*Module),
 ) ![]const u8 {
     var deps_str = std.ArrayList(u8).init(allocator);
     var it = deps.iterator();
@@ -1082,7 +955,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
     try addFlag(&zig_args, "llvm", self.use_llvm);
     try addFlag(&zig_args, "lld", self.use_lld);
 
-    if (self.root_module.target.ofmt) |ofmt| {
+    if (self.root_module.target.?.query.ofmt) |ofmt| {
         try zig_args.append(try std.fmt.allocPrint(b.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
     }
 
@@ -1110,7 +983,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
 
     {
         var seen_system_libs: std.StringHashMapUnmanaged(void) = .{};
-        var frameworks: std.StringArrayHashMapUnmanaged(Module.FrameworkLinkInfo) = .{};
+        var frameworks: std.StringArrayHashMapUnmanaged(Module.LinkFrameworkOptions) = .{};
 
         var prev_has_cflags = false;
         var prev_has_rcflags = false;
@@ -1240,7 +1113,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
                                 try zig_args.append(full_path_lib);
 
                                 if (other.linkage == Linkage.dynamic and
-                                    self.root_module.target_info.target.os.tag != .windows)
+                                    self.rootModuleTarget().os.tag != .windows)
                                 {
                                     if (fs.path.dirname(full_path_lib)) |dirname| {
                                         try zig_args.append("-rpath");
@@ -1471,11 +1344,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
             try zig_args.append(b.fmt("{}", .{version}));
         }
 
-        if (self.root_module.target_info.target.isDarwin()) {
+        if (self.rootModuleTarget().isDarwin()) {
             const install_name = self.install_name orelse b.fmt("@rpath/{s}{s}{s}", .{
-                self.root_module.target_info.target.libPrefix(),
+                self.rootModuleTarget().libPrefix(),
                 self.name,
-                self.root_module.target_info.target.dynamicLibSuffix(),
+                self.rootModuleTarget().dynamicLibSuffix(),
             });
             try zig_args.append("-install_name");
             try zig_args.append(install_name);
@@ -1763,7 +1636,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
     }
 
     if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and
-        self.version != null and self.root_module.target.wantSharedLibSymLinks())
+        self.version != null and std.Build.wantSharedLibSymLinks(self.rootModuleTarget()))
     {
         try doAtomicSymLinks(
             step,
@@ -1932,3 +1805,8 @@ fn matchCompileError(actual: []const u8, expected: []const u8) bool {
     }
     return false;
 }
+
+pub fn rootModuleTarget(c: *Compile) std.Target {
+    // The root module is always given a target, so we know this to be non-null.
+    return c.root_module.target.?.target;
+}
lib/std/Build/Step/ConfigHeader.zig
@@ -15,9 +15,6 @@ pub const Style = union(enum) {
     /// Start with nothing, like blank, and output a nasm .asm file.
     nasm,
 
-    /// deprecated: use `getPath`
-    pub const getFileSource = getPath;
-
     pub fn getPath(style: Style) ?std.Build.LazyPath {
         switch (style) {
             .autoconf, .cmake => |s| return s,
@@ -107,9 +104,6 @@ pub fn addValues(self: *ConfigHeader, values: anytype) void {
     return addValuesInner(self, values) catch @panic("OOM");
 }
 
-/// deprecated: use `getOutput`
-pub const getFileSource = getOutput;
-
 pub fn getOutput(self: *ConfigHeader) std.Build.LazyPath {
     return .{ .generated = &self.output_file };
 }
lib/std/Build/Step/InstallArtifact.zig
@@ -94,7 +94,7 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins
         .dylib_symlinks = if (options.dylib_symlinks orelse (dest_dir != null and
             artifact.isDynamicLibrary() and
             artifact.version != null and
-            artifact.target.wantSharedLibSymLinks())) .{
+            std.Build.wantSharedLibSymLinks(artifact.rootModuleTarget()))) .{
             .major_only_filename = artifact.major_only_filename.?,
             .name_only_filename = artifact.name_only_filename.?,
         } else null,
lib/std/Build/Step/Options.zig
@@ -165,9 +165,6 @@ fn printLiteral(out: anytype, val: anytype, indent: u8) !void {
     }
 }
 
-/// deprecated: use `addOptionPath`
-pub const addOptionFileSource = addOptionPath;
-
 /// The value is the path in the cache dir.
 /// Adds a dependency automatically.
 pub fn addOptionPath(
@@ -189,8 +186,7 @@ pub fn addOptionArtifact(self: *Options, name: []const u8, artifact: *Step.Compi
 
 pub fn createModule(self: *Options) *std.Build.Module {
     return self.step.owner.createModule(.{
-        .source_file = self.getOutput(),
-        .dependencies = &.{},
+        .root_source_file = self.getOutput(),
     });
 }
 
lib/std/Build/Step/Run.zig
@@ -197,16 +197,10 @@ pub fn addPrefixedOutputFileArg(
     return .{ .generated = &output.generated_file };
 }
 
-/// deprecated: use `addFileArg`
-pub const addFileSourceArg = addFileArg;
-
 pub fn addFileArg(self: *Run, lp: std.Build.LazyPath) void {
     self.addPrefixedFileArg("", lp);
 }
 
-// deprecated: use `addPrefixedFileArg`
-pub const addPrefixedFileSourceArg = addPrefixedFileArg;
-
 pub fn addPrefixedFileArg(self: *Run, prefix: []const u8, lp: std.Build.LazyPath) void {
     const b = self.step.owner;
 
@@ -488,7 +482,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
                 man.hash.addBytes(file_path);
             },
             .artifact => |artifact| {
-                if (artifact.root_module.target_info.target.os.tag == .windows) {
+                if (artifact.rootModuleTarget().os.tag == .windows) {
                     // On Windows we don't have rpaths so we have to add .dll search paths to PATH
                     self.addPathForDynLibs(artifact);
                 }
@@ -682,9 +676,10 @@ fn runCommand(
                 else => break :interpret,
             }
 
-            const need_cross_glibc = exe.root_module.target_info.target.isGnuLibC() and
+            const need_cross_glibc = exe.rootModuleTarget().isGnuLibC() and
                 exe.is_linking_libc;
-            switch (b.host.getExternalExecutor(&exe.root_module.target_info, .{
+            const other_target_info = exe.root_module.target.?.toNativeTargetInfo();
+            switch (b.host.toNativeTargetInfo().getExternalExecutor(&other_target_info, .{
                 .qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null,
                 .link_libc = exe.is_linking_libc,
             })) {
@@ -715,9 +710,9 @@ fn runCommand(
                             // needs the directory to be called "i686" rather than
                             // "x86" which is why we do it manually here.
                             const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
-                            const cpu_arch = exe.root_module.target_info.target.cpu.arch;
-                            const os_tag = exe.root_module.target_info.target.os.tag;
-                            const abi = exe.root_module.target_info.target.abi;
+                            const cpu_arch = exe.rootModuleTarget().cpu.arch;
+                            const os_tag = exe.rootModuleTarget().os.tag;
+                            const abi = exe.rootModuleTarget().abi;
                             const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
                                 "i686"
                             else
@@ -770,7 +765,7 @@ fn runCommand(
                     if (allow_skip) return error.MakeSkipped;
 
                     const host_name = try b.host.target.zigTriple(b.allocator);
-                    const foreign_name = try exe.root_module.target_info.target.zigTriple(b.allocator);
+                    const foreign_name = try exe.rootModuleTarget().zigTriple(b.allocator);
 
                     return step.fail("the host system ({s}) is unable to execute binaries from the target ({s})", .{
                         host_name, foreign_name,
@@ -778,7 +773,7 @@ fn runCommand(
                 },
             }
 
-            if (exe.root_module.target_info.target.os.tag == .windows) {
+            if (exe.rootModuleTarget().os.tag == .windows) {
                 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
                 self.addPathForDynLibs(exe);
             }
@@ -1300,7 +1295,7 @@ fn addPathForDynLibs(self: *Run, artifact: *Step.Compile) void {
     while (it.next()) |item| {
         const other = item.compile.?;
         if (item.module == &other.root_module) {
-            if (item.module.target_info.target.os.tag == .windows and other.isDynamicLibrary()) {
+            if (item.module.target.?.target.os.tag == .windows and other.isDynamicLibrary()) {
                 addPathDir(self, fs.path.dirname(other.getEmittedBin().getPath(b)).?);
                 addPathForDynLibs(self, other);
             }
@@ -1321,7 +1316,7 @@ fn failForeign(
 
             const b = self.step.owner;
             const host_name = try b.host.target.zigTriple(b.allocator);
-            const foreign_name = try exe.root_module.target_info.target.zigTriple(b.allocator);
+            const foreign_name = try exe.rootModuleTarget().zigTriple(b.allocator);
 
             return self.step.fail(
                 \\unable to spawn foreign binary '{s}' ({s}) on host system ({s})
lib/std/Build/Step/TranslateC.zig
@@ -2,7 +2,6 @@ const std = @import("std");
 const Step = std.Build.Step;
 const fs = std.fs;
 const mem = std.mem;
-const CrossTarget = std.zig.CrossTarget;
 
 const TranslateC = @This();
 
@@ -13,7 +12,7 @@ source: std.Build.LazyPath,
 include_dirs: std.ArrayList([]const u8),
 c_macros: std.ArrayList([]const u8),
 out_basename: []const u8,
-target: CrossTarget,
+target: std.Build.ResolvedTarget,
 optimize: std.builtin.OptimizeMode,
 output_file: std.Build.GeneratedFile,
 link_libc: bool,
@@ -21,7 +20,7 @@ use_clang: bool,
 
 pub const Options = struct {
     source_file: std.Build.LazyPath,
-    target: CrossTarget,
+    target: std.Build.ResolvedTarget,
     optimize: std.builtin.OptimizeMode,
     link_libc: bool = true,
     use_clang: bool = true,
@@ -54,7 +53,7 @@ pub fn create(owner: *std.Build, options: Options) *TranslateC {
 pub const AddExecutableOptions = struct {
     name: ?[]const u8 = null,
     version: ?std.SemanticVersion = null,
-    target: ?CrossTarget = null,
+    target: ?std.Build.ResolvedTarget = null,
     optimize: ?std.builtin.OptimizeMode = null,
     linkage: ?Step.Compile.Linkage = null,
 };
@@ -139,9 +138,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
 
     try argv_list.append("--listen=-");
 
-    if (!self.target.isNative()) {
+    if (!self.target.query.isNative()) {
         try argv_list.append("-target");
-        try argv_list.append(try self.target.zigTriple(b.allocator));
+        try argv_list.append(try self.target.query.zigTriple(b.allocator));
     }
 
     switch (self.optimize) {
lib/std/Build/Step/WriteFile.zig
@@ -28,9 +28,6 @@ pub const File = struct {
     sub_path: []const u8,
     contents: Contents,
 
-    /// deprecated: use `getPath`
-    pub const getFileSource = getPath;
-
     pub fn getPath(self: *File) std.Build.LazyPath {
         return .{ .generated = &self.generated_file };
     }
@@ -126,10 +123,6 @@ pub fn addBytesToSource(wf: *WriteFile, bytes: []const u8, sub_path: []const u8)
     }) catch @panic("OOM");
 }
 
-pub const getFileSource = @compileError("Deprecated; use the return value from add()/addCopyFile(), or use files[i].getPath()");
-
-pub const getDirectorySource = getDirectory;
-
 /// Returns a `LazyPath` representing the base directory that contains all the
 /// files from this `WriteFile`.
 pub fn getDirectory(wf: *WriteFile) std.Build.LazyPath {
lib/std/Build/Cache.zig
@@ -270,7 +270,7 @@ pub const HashHelper = struct {
                     .none => {},
                 }
             },
-            std.Build.Step.Compile.BuildId => switch (x) {
+            std.zig.BuildId => switch (x) {
                 .none, .fast, .uuid, .sha1, .md5 => hh.add(std.meta.activeTag(x)),
                 .hexstring => |hex_string| hh.addBytes(hex_string.toSlice()),
             },
lib/std/Build/Module.zig
@@ -3,27 +3,24 @@ owner: *std.Build,
 /// Tracks the set of steps that depend on this `Module`. This ensures that
 /// when making this `Module` depend on other `Module` objects and `Step`
 /// objects, respective `Step` dependencies can be added.
-depending_steps: std.AutoArrayHashMapUnmanaged(*std.Build.Step.Compile, void),
-/// This could either be a generated file, in which case the module
-/// contains exactly one file, or it could be a path to the root source
-/// file of directory of files which constitute the module.
-/// If `null`, it means this module is made up of only `link_objects`.
+depending_steps: std.AutoArrayHashMapUnmanaged(*Step.Compile, void),
 root_source_file: ?LazyPath,
 /// The modules that are mapped into this module's import table.
-import_table: std.StringArrayHashMap(*Module),
+/// Use `addImport` rather than modifying this field directly in order to
+/// maintain step dependency edges.
+import_table: std.StringArrayHashMapUnmanaged(*Module),
 
-target: std.zig.CrossTarget,
-target_info: NativeTargetInfo,
-optimize: std.builtin.OptimizeMode,
+target: ?std.Build.ResolvedTarget = null,
+optimize: ?std.builtin.OptimizeMode = null,
 dwarf_format: ?std.dwarf.Format,
 
-c_macros: std.ArrayList([]const u8),
-include_dirs: std.ArrayList(IncludeDir),
-lib_paths: std.ArrayList(LazyPath),
-rpaths: std.ArrayList(LazyPath),
-frameworks: std.StringArrayHashMapUnmanaged(FrameworkLinkInfo),
+c_macros: std.ArrayListUnmanaged([]const u8),
+include_dirs: std.ArrayListUnmanaged(IncludeDir),
+lib_paths: std.ArrayListUnmanaged(LazyPath),
+rpaths: std.ArrayListUnmanaged(RPath),
+frameworks: std.StringArrayHashMapUnmanaged(LinkFrameworkOptions),
 c_std: std.Build.CStd,
-link_objects: std.ArrayList(LinkObject),
+link_objects: std.ArrayListUnmanaged(LinkObject),
 
 strip: ?bool,
 unwind_tables: ?bool,
@@ -53,9 +50,14 @@ link_libcpp: ?bool,
 /// Symbols to be exported when compiling to WebAssembly.
 export_symbol_names: []const []const u8 = &.{},
 
+pub const RPath = union(enum) {
+    lazy_path: LazyPath,
+    special: []const u8,
+};
+
 pub const LinkObject = union(enum) {
     static_path: LazyPath,
-    other_step: *std.Build.Step.Compile,
+    other_step: *Step.Compile,
     system_lib: SystemLib,
     assembly_file: LazyPath,
     c_source_file: *CSourceFile,
@@ -133,21 +135,32 @@ pub const IncludeDir = union(enum) {
     path_after: LazyPath,
     framework_path: LazyPath,
     framework_path_system: LazyPath,
-    other_step: *std.Build.Step.Compile,
-    config_header_step: *std.Build.Step.ConfigHeader,
+    other_step: *Step.Compile,
+    config_header_step: *Step.ConfigHeader,
 };
 
-pub const FrameworkLinkInfo = struct {
+pub const LinkFrameworkOptions = struct {
     needed: bool = false,
     weak: bool = false,
 };
 
+/// Unspecified options here will be inherited from parent `Module` when
+/// inserted into an import table.
 pub const CreateOptions = struct {
-    target: std.zig.CrossTarget,
-    target_info: ?NativeTargetInfo = null,
-    optimize: std.builtin.OptimizeMode,
+    /// This could either be a generated file, in which case the module
+    /// contains exactly one file, or it could be a path to the root source
+    /// file of directory of files which constitute the module.
+    /// If `null`, it means this module is made up of only `link_objects`.
     root_source_file: ?LazyPath = null,
-    import_table: []const Import = &.{},
+
+    /// The table of other modules that this module can access via `@import`.
+    /// Imports are allowed to be cyclical, so this table can be added to after
+    /// the `Module` is created via `addImport`.
+    imports: []const Import = &.{},
+
+    target: ?std.Build.ResolvedTarget = null,
+    optimize: ?std.builtin.OptimizeMode = null,
+
     link_libc: ?bool = null,
     link_libcpp: ?bool = null,
     single_threaded: ?bool = null,
@@ -173,26 +186,26 @@ pub const Import = struct {
     module: *Module,
 };
 
-pub fn init(owner: *std.Build, options: CreateOptions, compile: ?*std.Build.Step.Compile) Module {
-    var m: Module = .{
+pub fn init(m: *Module, owner: *std.Build, options: CreateOptions, compile: ?*Step.Compile) void {
+    const allocator = owner.allocator;
+
+    m.* = .{
         .owner = owner,
         .depending_steps = .{},
         .root_source_file = if (options.root_source_file) |lp| lp.dupe(owner) else null,
-        .import_table = std.StringArrayHashMap(*Module).init(owner.allocator),
+        .import_table = .{},
         .target = options.target,
-        .target_info = options.target_info orelse
-            NativeTargetInfo.detect(options.target) catch @panic("unhandled error"),
         .optimize = options.optimize,
         .link_libc = options.link_libc,
         .link_libcpp = options.link_libcpp,
         .dwarf_format = options.dwarf_format,
-        .c_macros = std.ArrayList([]const u8).init(owner.allocator),
-        .include_dirs = std.ArrayList(IncludeDir).init(owner.allocator),
-        .lib_paths = std.ArrayList(LazyPath).init(owner.allocator),
-        .rpaths = std.ArrayList(LazyPath).init(owner.allocator),
+        .c_macros = .{},
+        .include_dirs = .{},
+        .lib_paths = .{},
+        .rpaths = .{},
         .frameworks = .{},
         .c_std = options.c_std,
-        .link_objects = std.ArrayList(LinkObject).init(owner.allocator),
+        .link_objects = .{},
         .strip = options.strip,
         .unwind_tables = options.unwind_tables,
         .single_threaded = options.single_threaded,
@@ -208,31 +221,30 @@ pub fn init(owner: *std.Build, options: CreateOptions, compile: ?*std.Build.Step
         .export_symbol_names = &.{},
     };
 
-    if (compile) |c| {
-        m.depending_steps.put(owner.allocator, c, {}) catch @panic("OOM");
+    m.import_table.ensureUnusedCapacity(allocator, options.imports.len) catch @panic("OOM");
+    for (options.imports) |dep| {
+        m.import_table.putAssumeCapacity(dep.name, dep.module);
     }
 
-    m.import_table.ensureUnusedCapacity(options.import_table.len) catch @panic("OOM");
-    for (options.import_table) |dep| {
-        m.import_table.putAssumeCapacity(dep.name, dep.module);
+    if (compile) |c| {
+        m.depending_steps.put(allocator, c, {}) catch @panic("OOM");
     }
 
+    // This logic accesses `depending_steps` which was just modified above.
     var it = m.iterateDependencies(null);
-    while (it.next()) |item| addShallowDependencies(&m, item.module);
-
-    return m;
+    while (it.next()) |item| addShallowDependencies(m, item.module);
 }
 
 pub fn create(owner: *std.Build, options: CreateOptions) *Module {
     const m = owner.allocator.create(Module) catch @panic("OOM");
-    m.* = init(owner, options, null);
+    m.init(owner, options, null);
     return m;
 }
 
 /// Adds an existing module to be used with `@import`.
 pub fn addImport(m: *Module, name: []const u8, module: *Module) void {
     const b = m.owner;
-    m.import_table.put(b.dupe(name), module) catch @panic("OOM");
+    m.import_table.put(b.allocator, b.dupe(name), module) catch @panic("OOM");
 
     var it = module.iterateDependencies(null);
     while (it.next()) |item| addShallowDependencies(m, item.module);
@@ -244,7 +256,10 @@ pub fn addImport(m: *Module, name: []const u8, module: *Module) void {
 fn addShallowDependencies(m: *Module, dependee: *Module) void {
     if (dependee.root_source_file) |lazy_path| addLazyPathDependencies(m, dependee, lazy_path);
     for (dependee.lib_paths.items) |lib_path| addLazyPathDependencies(m, dependee, lib_path);
-    for (dependee.rpaths.items) |rpath| addLazyPathDependencies(m, dependee, rpath);
+    for (dependee.rpaths.items) |rpath| switch (rpath) {
+        .lazy_path => |lp| addLazyPathDependencies(m, dependee, lp),
+        .special => {},
+    };
 
     for (dependee.link_objects.items) |link_object| switch (link_object) {
         .other_step => |compile| addStepDependencies(m, dependee, &compile.step),
@@ -277,7 +292,7 @@ fn addLazyPathDependenciesOnly(m: *Module, lazy_path: LazyPath) void {
     }
 }
 
-fn addStepDependencies(m: *Module, module: *Module, dependee: *std.Build.Step) void {
+fn addStepDependencies(m: *Module, module: *Module, dependee: *Step) void {
     addStepDependenciesOnly(m, dependee);
     if (m != module) {
         for (m.depending_steps.keys()) |compile| {
@@ -286,20 +301,19 @@ fn addStepDependencies(m: *Module, module: *Module, dependee: *std.Build.Step) v
     }
 }
 
-fn addStepDependenciesOnly(m: *Module, dependee: *std.Build.Step) void {
+fn addStepDependenciesOnly(m: *Module, dependee: *Step) void {
     for (m.depending_steps.keys()) |compile| {
         compile.step.dependOn(dependee);
     }
 }
 
 /// Creates a new module and adds it to be used with `@import`.
-pub fn addAnonymousImport(m: *Module, name: []const u8, options: std.Build.CreateModuleOptions) void {
-    const b = m.step.owner;
-    const module = b.createModule(options);
+pub fn addAnonymousImport(m: *Module, name: []const u8, options: CreateOptions) void {
+    const module = create(m.owner, options);
     return addImport(m, name, module);
 }
 
-pub fn addOptions(m: *Module, module_name: []const u8, options: *std.Build.Step.Options) void {
+pub fn addOptions(m: *Module, module_name: []const u8, options: *Step.Options) void {
     addImport(m, module_name, options.createModule());
 }
 
@@ -311,14 +325,14 @@ pub const DependencyIterator = struct {
     pub const Key = struct {
         /// The compilation that contains the `Module`. Note that a `Module` might be
         /// used by more than one compilation.
-        compile: ?*std.Build.Step.Compile,
+        compile: ?*Step.Compile,
         module: *Module,
     };
 
     pub const Item = struct {
         /// The compilation that contains the `Module`. Note that a `Module` might be
         /// used by more than one compilation.
-        compile: ?*std.Build.Step.Compile,
+        compile: ?*Step.Compile,
         module: *Module,
         name: []const u8,
     };
@@ -368,7 +382,7 @@ pub const DependencyIterator = struct {
 
 pub fn iterateDependencies(
     m: *Module,
-    chase_steps: ?*std.Build.Step.Compile,
+    chase_steps: ?*Step.Compile,
 ) DependencyIterator {
     var it: DependencyIterator = .{
         .allocator = m.owner.allocator,
@@ -397,16 +411,18 @@ pub fn linkSystemLibrary(
     options: LinkSystemLibraryOptions,
 ) void {
     const b = m.owner;
-    if (m.target_info.target.is_libc_lib_name(name)) {
+
+    const target = m.requireKnownTarget();
+    if (target.is_libc_lib_name(name)) {
         m.link_libc = true;
         return;
     }
-    if (m.target_info.target.is_libcpp_lib_name(name)) {
+    if (target.is_libcpp_lib_name(name)) {
         m.link_libcpp = true;
         return;
     }
 
-    m.link_objects.append(.{
+    m.link_objects.append(b.allocator, .{
         .system_lib = .{
             .name = b.dupe(name),
             .needed = options.needed,
@@ -418,6 +434,11 @@ pub fn linkSystemLibrary(
     }) catch @panic("OOM");
 }
 
+pub fn linkFramework(m: *Module, name: []const u8, options: LinkFrameworkOptions) void {
+    const b = m.owner;
+    m.frameworks.put(b.allocator, b.dupe(name), options) catch @panic("OOM");
+}
+
 pub const AddCSourceFilesOptions = struct {
     /// When provided, `files` are relative to `dependency` rather than the
     /// package that owns the `Compile` step.
@@ -428,19 +449,23 @@ pub const AddCSourceFilesOptions = struct {
 
 /// Handy when you have many C/C++ source files and want them all to have the same flags.
 pub fn addCSourceFiles(m: *Module, options: AddCSourceFilesOptions) void {
-    const c_source_files = m.owner.allocator.create(CSourceFiles) catch @panic("OOM");
+    const b = m.owner;
+    const allocator = b.allocator;
+    const c_source_files = allocator.create(CSourceFiles) catch @panic("OOM");
     c_source_files.* = .{
         .dependency = options.dependency,
-        .files = m.owner.dupeStrings(options.files),
-        .flags = m.owner.dupeStrings(options.flags),
+        .files = b.dupeStrings(options.files),
+        .flags = b.dupeStrings(options.flags),
     };
-    m.link_objects.append(.{ .c_source_files = c_source_files }) catch @panic("OOM");
+    m.link_objects.append(allocator, .{ .c_source_files = c_source_files }) catch @panic("OOM");
 }
 
 pub fn addCSourceFile(m: *Module, source: CSourceFile) void {
-    const c_source_file = m.owner.allocator.create(CSourceFile) catch @panic("OOM");
-    c_source_file.* = source.dupe(m.owner);
-    m.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM");
+    const b = m.owner;
+    const allocator = b.allocator;
+    const c_source_file = allocator.create(CSourceFile) catch @panic("OOM");
+    c_source_file.* = source.dupe(b);
+    m.link_objects.append(allocator, .{ .c_source_file = c_source_file }) catch @panic("OOM");
     addLazyPathDependenciesOnly(m, source.file);
 }
 
@@ -448,30 +473,122 @@ pub fn addCSourceFile(m: *Module, source: CSourceFile) void {
 /// Can be called regardless of target. The .rc file will be ignored
 /// if the target object format does not support embedded resources.
 pub fn addWin32ResourceFile(m: *Module, source: RcSourceFile) void {
+    const b = m.owner;
+    const allocator = b.allocator;
+    const target = m.requireKnownTarget();
     // Only the PE/COFF format has a Resource Table, so for any other target
     // the resource file is ignored.
-    if (m.target_info.target.ofmt != .coff) return;
+    if (target.ofmt != .coff) return;
 
-    const rc_source_file = m.owner.allocator.create(RcSourceFile) catch @panic("OOM");
-    rc_source_file.* = source.dupe(m.owner);
-    m.link_objects.append(.{ .win32_resource_file = rc_source_file }) catch @panic("OOM");
+    const rc_source_file = allocator.create(RcSourceFile) catch @panic("OOM");
+    rc_source_file.* = source.dupe(b);
+    m.link_objects.append(allocator, .{ .win32_resource_file = rc_source_file }) catch @panic("OOM");
     addLazyPathDependenciesOnly(m, source.file);
 }
 
 pub fn addAssemblyFile(m: *Module, source: LazyPath) void {
-    m.link_objects.append(.{ .assembly_file = source.dupe(m.owner) }) catch @panic("OOM");
+    const b = m.owner;
+    m.link_objects.append(b.allocator, .{ .assembly_file = source.dupe(b) }) catch @panic("OOM");
     addLazyPathDependenciesOnly(m, source);
 }
 
-pub fn addObjectFile(m: *Module, source: LazyPath) void {
-    m.link_objects.append(.{ .static_path = source.dupe(m.owner) }) catch @panic("OOM");
-    addLazyPathDependencies(m, source);
+pub fn addObjectFile(m: *Module, object: LazyPath) void {
+    const b = m.owner;
+    m.link_objects.append(b.allocator, .{ .static_path = object.dupe(b) }) catch @panic("OOM");
+    addLazyPathDependenciesOnly(m, object);
+}
+
+pub fn addObject(m: *Module, object: *Step.Compile) void {
+    assert(object.kind == .obj);
+    m.linkLibraryOrObject(object);
+}
+
+pub fn linkLibrary(m: *Module, library: *Step.Compile) void {
+    assert(library.kind == .lib);
+    m.linkLibraryOrObject(library);
+}
+
+pub fn addAfterIncludePath(m: *Module, lazy_path: LazyPath) void {
+    const b = m.owner;
+    m.include_dirs.append(b.allocator, .{ .path_after = lazy_path.dupe(b) }) catch @panic("OOM");
+    addLazyPathDependenciesOnly(m, lazy_path);
+}
+
+pub fn addSystemIncludePath(m: *Module, lazy_path: LazyPath) void {
+    const b = m.owner;
+    m.include_dirs.append(b.allocator, .{ .path_system = lazy_path.dupe(b) }) catch @panic("OOM");
+    addLazyPathDependenciesOnly(m, lazy_path);
+}
+
+pub fn addIncludePath(m: *Module, lazy_path: LazyPath) void {
+    const b = m.owner;
+    m.include_dirs.append(b.allocator, .{ .path = lazy_path.dupe(b) }) catch @panic("OOM");
+    addLazyPathDependenciesOnly(m, lazy_path);
+}
+
+pub fn addConfigHeader(m: *Module, config_header: *Step.ConfigHeader) void {
+    const allocator = m.owner.allocator;
+    m.include_dirs.append(allocator, .{ .config_header_step = config_header }) catch @panic("OOM");
+    addStepDependenciesOnly(m, &config_header.step);
+}
+
+pub fn addSystemFrameworkPath(m: *Module, directory_path: LazyPath) void {
+    const b = m.owner;
+    m.include_dirs.append(b.allocator, .{ .framework_path_system = directory_path.dupe(b) }) catch
+        @panic("OOM");
+    addLazyPathDependenciesOnly(m, directory_path);
+}
+
+pub fn addFrameworkPath(m: *Module, directory_path: LazyPath) void {
+    const b = m.owner;
+    m.include_dirs.append(b.allocator, .{ .framework_path = directory_path.dupe(b) }) catch
+        @panic("OOM");
+    addLazyPathDependenciesOnly(m, directory_path);
+}
+
+pub fn addLibraryPath(m: *Module, directory_path: LazyPath) void {
+    const b = m.owner;
+    m.lib_paths.append(b.allocator, directory_path.dupe(b)) catch @panic("OOM");
+    addLazyPathDependenciesOnly(m, directory_path);
+}
+
+pub fn addRPath(m: *Module, directory_path: LazyPath) void {
+    const b = m.owner;
+    switch (directory_path) {
+        .path, .cwd_relative => |path| {
+            // TODO: remove this check after people upgrade and stop expecting it to work
+            if (std.mem.startsWith(u8, path, "@executable_path") or
+                std.mem.startsWith(u8, path, "@loader_path"))
+            {
+                @panic("this function is for adding directory paths. It does not support special rpaths. use addRPathSpecial for that.");
+            }
+        },
+        else => {},
+    }
+    m.rpaths.append(b.allocator, .{ .lazy_path = directory_path.dupe(b) }) catch @panic("OOM");
+    addLazyPathDependenciesOnly(m, directory_path);
+}
+
+pub fn addRPathSpecial(m: *Module, bytes: []const u8) void {
+    const b = m.owner;
+    m.rpaths.append(b.allocator, .{ .special = b.dupe(bytes) }) catch @panic("OOM");
+}
+
+/// Equvialent to the following C code, applied to all C source files owned by
+/// this `Module`:
+/// ```c
+/// #define name value
+/// ```
+/// `name` and `value` need not live longer than the function call.
+pub fn addCMacro(m: *Module, name: []const u8, value: []const u8) void {
+    const b = m.owner;
+    m.c_macros.append(b.allocator, b.fmt("-D{s}={s}", .{ name, value })) catch @panic("OOM");
 }
 
 pub fn appendZigProcessFlags(
     m: *Module,
     zig_args: *std.ArrayList([]const u8),
-    asking_step: ?*std.Build.Step,
+    asking_step: ?*Step,
 ) !void {
     const b = m.owner;
 
@@ -495,27 +612,30 @@ pub fn appendZigProcessFlags(
     }
 
     try zig_args.ensureUnusedCapacity(1);
-    switch (m.optimize) {
-        .Debug => {}, // Skip since it's the default.
+    if (m.optimize) |optimize| switch (optimize) {
+        .Debug => zig_args.appendAssumeCapacity("-ODebug"),
         .ReleaseSmall => zig_args.appendAssumeCapacity("-OReleaseSmall"),
         .ReleaseFast => zig_args.appendAssumeCapacity("-OReleaseFast"),
         .ReleaseSafe => zig_args.appendAssumeCapacity("-OReleaseSafe"),
-    }
+    };
 
     if (m.code_model != .default) {
         try zig_args.append("-mcmodel");
         try zig_args.append(@tagName(m.code_model));
     }
 
-    if (!m.target.isNative()) {
-        try zig_args.appendSlice(&.{
-            "-target", try m.target.zigTriple(b.allocator),
-            "-mcpu",   try std.Build.serializeCpu(b.allocator, m.target.getCpu()),
-        });
-
-        if (m.target.dynamic_linker.get()) |dynamic_linker| {
-            try zig_args.append("--dynamic-linker");
-            try zig_args.append(dynamic_linker);
+    if (m.target) |target| {
+        // Communicate the query via CLI since it's more compact.
+        if (!target.query.isNative()) {
+            try zig_args.appendSlice(&.{
+                "-target", try target.query.zigTriple(b.allocator),
+                "-mcpu",   try std.Build.serializeCpu(b.allocator, target.query.getCpu()),
+            });
+
+            if (target.query.dynamic_linker.get()) |dynamic_linker| {
+                try zig_args.append("--dynamic-linker");
+                try zig_args.append(dynamic_linker);
+            }
         }
     }
 
@@ -565,10 +685,7 @@ pub fn appendZigProcessFlags(
         }
     }
 
-    for (m.c_macros.items) |c_macro| {
-        try zig_args.append("-D");
-        try zig_args.append(c_macro);
-    }
+    try zig_args.appendSlice(m.c_macros.items);
 
     try zig_args.ensureUnusedCapacity(2 * m.lib_paths.items.len);
     for (m.lib_paths.items) |lib_path| {
@@ -577,26 +694,16 @@ pub fn appendZigProcessFlags(
     }
 
     try zig_args.ensureUnusedCapacity(2 * m.rpaths.items.len);
-    for (m.rpaths.items) |rpath| {
-        zig_args.appendAssumeCapacity("-rpath");
-
-        if (m.target_info.target.isDarwin()) switch (rpath) {
-            .path, .cwd_relative => |path| {
-                // On Darwin, we should not try to expand special runtime paths such as
-                // * @executable_path
-                // * @loader_path
-                if (std.mem.startsWith(u8, path, "@executable_path") or
-                    std.mem.startsWith(u8, path, "@loader_path"))
-                {
-                    zig_args.appendAssumeCapacity(path);
-                    continue;
-                }
-            },
-            .generated, .dependency => {},
-        };
-
-        zig_args.appendAssumeCapacity(rpath.getPath2(b, asking_step));
-    }
+    for (m.rpaths.items) |rpath| switch (rpath) {
+        .lazy_path => |lp| {
+            zig_args.appendAssumeCapacity("-rpath");
+            zig_args.appendAssumeCapacity(lp.getPath2(b, asking_step));
+        },
+        .special => |bytes| {
+            zig_args.appendAssumeCapacity("-rpath");
+            zig_args.appendAssumeCapacity(bytes);
+        },
+    };
 }
 
 fn addFlag(
@@ -609,8 +716,32 @@ fn addFlag(
     return args.append(if (cond) then_name else else_name);
 }
 
+fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void {
+    const allocator = m.owner.allocator;
+    _ = other.getEmittedBin(); // Indicate there is a dependency on the outputted binary.
+    addStepDependenciesOnly(m, &other.step);
+
+    if (other.rootModuleTarget().os.tag == .windows and other.isDynamicLibrary()) {
+        _ = other.getEmittedImplib(); // Indicate dependency on the outputted implib.
+    }
+
+    m.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM");
+    m.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM");
+
+    for (other.installed_headers.items) |install_step| {
+        addStepDependenciesOnly(m, install_step);
+    }
+}
+
+fn requireKnownTarget(m: *Module) std.Target {
+    const resolved_target = m.target orelse
+        @panic("this API requires the Module to be created with a known 'target' field");
+    return resolved_target.target;
+}
+
 const Module = @This();
 const std = @import("std");
 const assert = std.debug.assert;
 const LazyPath = std.Build.LazyPath;
 const NativeTargetInfo = std.zig.system.NativeTargetInfo;
+const Step = std.Build.Step;
lib/std/zig/system/NativeTargetInfo.zig
@@ -1008,8 +1008,8 @@ pub const GetExternalExecutorOptions = struct {
     link_libc: bool = false,
 };
 
-/// Return whether or not the given host target is capable of executing natively executables
-/// of the other target.
+/// Return whether or not the given host is capable of running executables of
+/// the other target.
 pub fn getExternalExecutor(
     host: NativeTargetInfo,
     candidate: *const NativeTargetInfo,
lib/std/zig/CrossTarget.zig
@@ -632,10 +632,6 @@ pub fn linuxTriple(self: CrossTarget, allocator: mem.Allocator) ![]u8 {
     return Target.linuxTripleSimple(allocator, self.getCpuArch(), self.getOsTag(), self.getAbi());
 }
 
-pub fn wantSharedLibSymLinks(self: CrossTarget) bool {
-    return self.getOsTag() != .windows;
-}
-
 pub fn isGnuLibC(self: CrossTarget) bool {
     return Target.isGnuLibC_os_tag_abi(self.getOsTag(), self.getAbi());
 }
lib/std/Build.zig
@@ -14,20 +14,11 @@ const process = std.process;
 const EnvMap = std.process.EnvMap;
 const fmt_lib = std.fmt;
 const File = std.fs.File;
-const CrossTarget = std.zig.CrossTarget;
-const NativeTargetInfo = std.zig.system.NativeTargetInfo;
+const TargetQuery = std.zig.CrossTarget;
 const Sha256 = std.crypto.hash.sha2.Sha256;
 const Build = @This();
 
 pub const Cache = @import("Build/Cache.zig");
-
-/// deprecated: use `Step.Compile`.
-pub const LibExeObjStep = Step.Compile;
-/// deprecated: use `Build`.
-pub const Builder = Build;
-/// deprecated: use `Step.InstallDir.Options`
-pub const InstallDirectoryOptions = Step.InstallDir.Options;
-
 pub const Step = @import("Build/Step.zig");
 pub const Module = @import("Build/Module.zig");
 
@@ -94,7 +85,7 @@ enable_wine: bool = false,
 glibc_runtimes_dir: ?[]const u8 = null,
 
 /// Information about the native target. Computed before build() is invoked.
-host: NativeTargetInfo,
+host: ResolvedTarget,
 
 dep_prefix: []const u8 = "",
 
@@ -220,7 +211,7 @@ pub fn create(
     build_root: Cache.Directory,
     cache_root: Cache.Directory,
     global_cache_root: Cache.Directory,
-    host: NativeTargetInfo,
+    host: ResolvedTarget,
     cache: *Cache,
     available_deps: AvailableDeps,
 ) !*Build {
@@ -384,7 +375,7 @@ fn userInputOptionsFromArgs(allocator: Allocator, args: anytype) UserInputOption
         const v = @field(args, field.name);
         const T = @TypeOf(v);
         switch (T) {
-            CrossTarget => {
+            TargetQuery => {
                 user_input_options.put(field.name, .{
                     .name = field.name,
                     .value = .{ .scalar = v.zigTriple(allocator) catch @panic("OOM") },
@@ -593,14 +584,22 @@ pub fn addOptions(self: *Build) *Step.Options {
 
 pub const ExecutableOptions = struct {
     name: []const u8,
+    /// If you want the executable to run on the same computer as the one
+    /// building the package, pass the `host` field of the package's `Build`
+    /// instance.
+    target: ResolvedTarget,
     root_source_file: ?LazyPath = null,
     version: ?std.SemanticVersion = null,
-    target: CrossTarget = .{},
     optimize: std.builtin.OptimizeMode = .Debug,
     linkage: ?Step.Compile.Linkage = null,
     max_rss: usize = 0,
     link_libc: ?bool = null,
     single_threaded: ?bool = null,
+    pic: ?bool = null,
+    strip: ?bool = null,
+    unwind_tables: ?bool = null,
+    omit_frame_pointer: ?bool = null,
+    sanitize_thread: ?bool = null,
     use_llvm: ?bool = null,
     use_lld: ?bool = null,
     zig_lib_dir: ?LazyPath = null,
@@ -621,6 +620,11 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *Step.Compile {
             .optimize = options.optimize,
             .link_libc = options.link_libc,
             .single_threaded = options.single_threaded,
+            .pic = options.pic,
+            .strip = options.strip,
+            .unwind_tables = options.unwind_tables,
+            .omit_frame_pointer = options.omit_frame_pointer,
+            .sanitize_thread = options.sanitize_thread,
         },
         .version = options.version,
         .kind = .exe,
@@ -636,11 +640,18 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *Step.Compile {
 pub const ObjectOptions = struct {
     name: []const u8,
     root_source_file: ?LazyPath = null,
-    target: CrossTarget,
+    /// To choose the same computer as the one building the package, pass the
+    /// `host` field of the package's `Build` instance.
+    target: ResolvedTarget,
     optimize: std.builtin.OptimizeMode,
     max_rss: usize = 0,
     link_libc: ?bool = null,
     single_threaded: ?bool = null,
+    pic: ?bool = null,
+    strip: ?bool = null,
+    unwind_tables: ?bool = null,
+    omit_frame_pointer: ?bool = null,
+    sanitize_thread: ?bool = null,
     use_llvm: ?bool = null,
     use_lld: ?bool = null,
     zig_lib_dir: ?LazyPath = null,
@@ -655,6 +666,11 @@ pub fn addObject(b: *Build, options: ObjectOptions) *Step.Compile {
             .optimize = options.optimize,
             .link_libc = options.link_libc,
             .single_threaded = options.single_threaded,
+            .pic = options.pic,
+            .strip = options.strip,
+            .unwind_tables = options.unwind_tables,
+            .omit_frame_pointer = options.omit_frame_pointer,
+            .sanitize_thread = options.sanitize_thread,
         },
         .kind = .obj,
         .max_rss = options.max_rss,
@@ -666,13 +682,20 @@ pub fn addObject(b: *Build, options: ObjectOptions) *Step.Compile {
 
 pub const SharedLibraryOptions = struct {
     name: []const u8,
+    /// To choose the same computer as the one building the package, pass the
+    /// `host` field of the package's `Build` instance.
+    target: ResolvedTarget,
+    optimize: std.builtin.OptimizeMode,
     root_source_file: ?LazyPath = null,
     version: ?std.SemanticVersion = null,
-    target: CrossTarget,
-    optimize: std.builtin.OptimizeMode,
     max_rss: usize = 0,
     link_libc: ?bool = null,
     single_threaded: ?bool = null,
+    pic: ?bool = null,
+    strip: ?bool = null,
+    unwind_tables: ?bool = null,
+    omit_frame_pointer: ?bool = null,
+    sanitize_thread: ?bool = null,
     use_llvm: ?bool = null,
     use_lld: ?bool = null,
     zig_lib_dir: ?LazyPath = null,
@@ -693,6 +716,11 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *Step.Compile
             .root_source_file = options.root_source_file,
             .link_libc = options.link_libc,
             .single_threaded = options.single_threaded,
+            .pic = options.pic,
+            .strip = options.strip,
+            .unwind_tables = options.unwind_tables,
+            .omit_frame_pointer = options.omit_frame_pointer,
+            .sanitize_thread = options.sanitize_thread,
         },
         .kind = .lib,
         .linkage = .dynamic,
@@ -708,12 +736,19 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *Step.Compile
 pub const StaticLibraryOptions = struct {
     name: []const u8,
     root_source_file: ?LazyPath = null,
-    target: CrossTarget,
+    /// To choose the same computer as the one building the package, pass the
+    /// `host` field of the package's `Build` instance.
+    target: ResolvedTarget,
     optimize: std.builtin.OptimizeMode,
     version: ?std.SemanticVersion = null,
     max_rss: usize = 0,
     link_libc: ?bool = null,
     single_threaded: ?bool = null,
+    pic: ?bool = null,
+    strip: ?bool = null,
+    unwind_tables: ?bool = null,
+    omit_frame_pointer: ?bool = null,
+    sanitize_thread: ?bool = null,
     use_llvm: ?bool = null,
     use_lld: ?bool = null,
     zig_lib_dir: ?LazyPath = null,
@@ -728,6 +763,11 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *Step.Compile
             .root_source_file = options.root_source_file,
             .link_libc = options.link_libc,
             .single_threaded = options.single_threaded,
+            .pic = options.pic,
+            .strip = options.strip,
+            .unwind_tables = options.unwind_tables,
+            .omit_frame_pointer = options.omit_frame_pointer,
+            .sanitize_thread = options.sanitize_thread,
         },
         .kind = .lib,
         .linkage = .static,
@@ -742,7 +782,7 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *Step.Compile
 pub const TestOptions = struct {
     name: []const u8 = "test",
     root_source_file: LazyPath,
-    target: CrossTarget = .{},
+    target: ?ResolvedTarget = null,
     optimize: std.builtin.OptimizeMode = .Debug,
     version: ?std.SemanticVersion = null,
     max_rss: usize = 0,
@@ -750,6 +790,11 @@ pub const TestOptions = struct {
     test_runner: ?[]const u8 = null,
     link_libc: ?bool = null,
     single_threaded: ?bool = null,
+    pic: ?bool = null,
+    strip: ?bool = null,
+    unwind_tables: ?bool = null,
+    omit_frame_pointer: ?bool = null,
+    sanitize_thread: ?bool = null,
     use_llvm: ?bool = null,
     use_lld: ?bool = null,
     zig_lib_dir: ?LazyPath = null,
@@ -761,10 +806,15 @@ pub fn addTest(b: *Build, options: TestOptions) *Step.Compile {
         .kind = .@"test",
         .root_module = .{
             .root_source_file = options.root_source_file,
-            .target = options.target,
+            .target = options.target orelse b.host,
             .optimize = options.optimize,
             .link_libc = options.link_libc,
             .single_threaded = options.single_threaded,
+            .pic = options.pic,
+            .strip = options.strip,
+            .unwind_tables = options.unwind_tables,
+            .omit_frame_pointer = options.omit_frame_pointer,
+            .sanitize_thread = options.sanitize_thread,
         },
         .max_rss = options.max_rss,
         .filter = options.filter,
@@ -778,7 +828,9 @@ pub fn addTest(b: *Build, options: TestOptions) *Step.Compile {
 pub const AssemblyOptions = struct {
     name: []const u8,
     source_file: LazyPath,
-    target: CrossTarget,
+    /// To choose the same computer as the one building the package, pass the
+    /// `host` field of the package's `Build` instance.
+    target: ResolvedTarget,
     optimize: std.builtin.OptimizeMode,
     max_rss: usize = 0,
     zig_lib_dir: ?LazyPath = null,
@@ -1061,7 +1113,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
                 return null;
             },
             .scalar => |s| {
-                if (Step.Compile.BuildId.parse(s)) |build_id| {
+                if (std.zig.BuildId.parse(s)) |build_id| {
                     return build_id;
                 } else |err| {
                     log.err("unable to parse option '-D{s}': {s}", .{ name, @errorName(err) });
@@ -1126,13 +1178,20 @@ pub fn standardOptimizeOption(self: *Build, options: StandardOptimizeOptionOptio
 }
 
 pub const StandardTargetOptionsArgs = struct {
-    whitelist: ?[]const CrossTarget = null,
+    whitelist: ?[]const TargetQuery = null,
 
-    default_target: CrossTarget = CrossTarget{},
+    default_target: TargetQuery = .{},
 };
 
+/// Exposes standard `zig build` options for choosing a target and additionally
+/// resolves the target query.
+pub fn standardTargetOptions(b: *Build, args: StandardTargetOptionsArgs) ResolvedTarget {
+    const query = b.standardTargetOptionsQueryOnly(args);
+    return b.resolveTargetQuery(query);
+}
+
 /// Exposes standard `zig build` options for choosing a target.
-pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) CrossTarget {
+pub fn standardTargetOptionsQueryOnly(self: *Build, args: StandardTargetOptionsArgs) TargetQuery {
     const maybe_triple = self.option(
         []const u8,
         "target",
@@ -1146,8 +1205,8 @@ pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) Cros
 
     const triple = maybe_triple orelse "native";
 
-    var diags: CrossTarget.ParseOptions.Diagnostics = .{};
-    const selected_target = CrossTarget.parse(.{
+    var diags: TargetQuery.ParseOptions.Diagnostics = .{};
+    const selected_target = TargetQuery.parse(.{
         .arch_os_abi = triple,
         .cpu_features = mcpu,
         .diagnostics = &diags,
@@ -1203,7 +1262,7 @@ pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) Cros
         // Make sure it's a match of one of the list.
         var mismatch_triple = true;
         var mismatch_cpu_features = true;
-        var whitelist_item = CrossTarget{};
+        var whitelist_item: TargetQuery = .{};
         for (list) |t| {
             mismatch_cpu_features = true;
             mismatch_triple = true;
@@ -1340,7 +1399,7 @@ pub fn addUserInputFlag(self: *Build, name_raw: []const u8) !bool {
 
 fn typeToEnum(comptime T: type) TypeId {
     return switch (T) {
-        Step.Compile.BuildId => .build_id,
+        std.zig.BuildId => .build_id,
         else => return switch (@typeInfo(T)) {
             .Int => .int,
             .Float => .float,
@@ -1408,7 +1467,7 @@ pub fn installFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8
     self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step);
 }
 
-pub fn installDirectory(self: *Build, options: InstallDirectoryOptions) void {
+pub fn installDirectory(self: *Build, options: Step.InstallDir.Options) void {
     self.getInstallStep().dependOn(&self.addInstallDirectory(options).step);
 }
 
@@ -1454,7 +1513,7 @@ pub fn addInstallFileWithDir(
     return Step.InstallFile.create(self, source.dupe(self), install_dir, dest_rel_path);
 }
 
-pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *Step.InstallDir {
+pub fn addInstallDirectory(self: *Build, options: Step.InstallDir.Options) *Step.InstallDir {
     return Step.InstallDir.create(self, options);
 }
 
@@ -1511,7 +1570,7 @@ pub fn fmt(self: *Build, comptime format: []const u8, args: anytype) []u8 {
 
 pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []const u8) ![]const u8 {
     // TODO report error for ambiguous situations
-    const exe_extension = @as(CrossTarget, .{}).exeFileExt();
+    const exe_extension = @as(TargetQuery, .{}).exeFileExt();
     for (self.search_prefixes.items) |search_prefix| {
         for (names) |name| {
             if (fs.path.isAbsolute(name)) {
@@ -1957,22 +2016,6 @@ pub fn dumpBadGetPathHelp(
     tty_config.setColor(w, .reset) catch {};
 }
 
-/// Allocates a new string for assigning a value to a named macro.
-/// If the value is omitted, it is set to 1.
-/// `name` and `value` need not live longer than the function call.
-pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 {
-    var macro = allocator.alloc(
-        u8,
-        name.len + if (value) |value_slice| value_slice.len + 1 else 0,
-    ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable;
-    @memcpy(macro[0..name.len], name);
-    if (value) |value_slice| {
-        macro[name.len] = '=';
-        @memcpy(macro[name.len + 1 ..][0..value_slice.len], value_slice);
-    }
-    return macro;
-}
-
 pub const InstallDir = union(enum) {
     prefix: void,
     lib: void,
@@ -2062,6 +2105,44 @@ pub fn hex64(x: u64) [16]u8 {
     return result;
 }
 
+/// A pair of target query and fully resolved target.
+/// This type is generally required by build system API that need to be given a
+/// target. The query is kept because the Zig toolchain needs to know which parts
+/// of the target are "native". This can apply to the CPU, the OS, or even the ABI.
+pub const ResolvedTarget = struct {
+    query: TargetQuery,
+    target: std.Target,
+    dynamic_linker: std.Target.DynamicLinker,
+
+    pub fn toNativeTargetInfo(self: ResolvedTarget) std.zig.system.NativeTargetInfo {
+        return .{
+            .target = self.target,
+            .dynamic_linker = self.dynamic_linker,
+        };
+    }
+};
+
+/// Converts a target query into a fully resolved target that can be passed to
+/// various parts of the API.
+pub fn resolveTargetQuery(b: *Build, query: TargetQuery) ResolvedTarget {
+    // This context will likely be required in the future when the target is
+    // resolved via a WASI API or via the build protocol.
+    _ = b;
+
+    const result = std.zig.system.NativeTargetInfo.detect(query) catch
+        @panic("unable to resolve target query");
+
+    return .{
+        .query = query,
+        .target = result.target,
+        .dynamic_linker = result.dynamic_linker,
+    };
+}
+
+pub fn wantSharedLibSymLinks(target: std.Target) bool {
+    return target.os.tag != .windows;
+}
+
 test {
     _ = Cache;
     _ = Step;
lib/std/std.zig
@@ -194,9 +194,6 @@ pub const zig = @import("zig.zig");
 
 pub const start = @import("start.zig");
 
-/// deprecated: use `Build`.
-pub const build = Build;
-
 const root = @import("root");
 const options_override = if (@hasDecl(root, "std_options")) root.std_options else struct {};
 
lib/std/zig.zig
@@ -203,6 +203,83 @@ pub fn binNameAlloc(allocator: std.mem.Allocator, options: BinNameOptions) error
     }
 }
 
+pub const BuildId = union(enum) {
+    none,
+    fast,
+    uuid,
+    sha1,
+    md5,
+    hexstring: HexString,
+
+    pub fn eql(a: BuildId, b: BuildId) bool {
+        const Tag = @TypeOf(BuildId).Union.tag_type.?;
+        const a_tag: Tag = a;
+        const b_tag: Tag = b;
+        if (a_tag != b_tag) return false;
+        return switch (a) {
+            .none, .fast, .uuid, .sha1, .md5 => true,
+            .hexstring => |a_hexstring| std.mem.eql(u8, a_hexstring.toSlice(), b.hexstring.toSlice()),
+        };
+    }
+
+    pub const HexString = struct {
+        bytes: [32]u8,
+        len: u8,
+
+        /// Result is byte values, *not* hex-encoded.
+        pub fn toSlice(hs: *const HexString) []const u8 {
+            return hs.bytes[0..hs.len];
+        }
+    };
+
+    /// Input is byte values, *not* hex-encoded.
+    /// Asserts `bytes` fits inside `HexString`
+    pub fn initHexString(bytes: []const u8) BuildId {
+        var result: BuildId = .{ .hexstring = .{
+            .bytes = undefined,
+            .len = @intCast(bytes.len),
+        } };
+        @memcpy(result.hexstring.bytes[0..bytes.len], bytes);
+        return result;
+    }
+
+    /// Converts UTF-8 text to a `BuildId`.
+    pub fn parse(text: []const u8) !BuildId {
+        if (std.mem.eql(u8, text, "none")) {
+            return .none;
+        } else if (std.mem.eql(u8, text, "fast")) {
+            return .fast;
+        } else if (std.mem.eql(u8, text, "uuid")) {
+            return .uuid;
+        } else if (std.mem.eql(u8, text, "sha1") or std.mem.eql(u8, text, "tree")) {
+            return .sha1;
+        } else if (std.mem.eql(u8, text, "md5")) {
+            return .md5;
+        } else if (std.mem.startsWith(u8, text, "0x")) {
+            var result: BuildId = .{ .hexstring = undefined };
+            const slice = try std.fmt.hexToBytes(&result.hexstring.bytes, text[2..]);
+            result.hexstring.len = @as(u8, @intCast(slice.len));
+            return result;
+        }
+        return error.InvalidBuildIdStyle;
+    }
+
+    test parse {
+        try std.testing.expectEqual(BuildId.md5, try parse("md5"));
+        try std.testing.expectEqual(BuildId.none, try parse("none"));
+        try std.testing.expectEqual(BuildId.fast, try parse("fast"));
+        try std.testing.expectEqual(BuildId.uuid, try parse("uuid"));
+        try std.testing.expectEqual(BuildId.sha1, try parse("sha1"));
+        try std.testing.expectEqual(BuildId.sha1, try parse("tree"));
+
+        try std.testing.expect(BuildId.initHexString("").eql(try parse("0x")));
+        try std.testing.expect(BuildId.initHexString("\x12\x34\x56").eql(try parse("0x123456")));
+        try std.testing.expectError(error.InvalidLength, parse("0x12-34"));
+        try std.testing.expectError(error.InvalidCharacter, parse("0xfoobbb"));
+        try std.testing.expectError(error.InvalidBuildIdStyle, parse("yaddaxxx"));
+    }
+};
+
 test {
     @import("std").testing.refAllDecls(@This());
 }
lib/build_runner.zig
@@ -46,7 +46,12 @@ pub fn main() !void {
         return error.InvalidArgs;
     };
 
-    const host = try std.zig.system.NativeTargetInfo.detect(.{});
+    const detected = try std.zig.system.NativeTargetInfo.detect(.{});
+    const host: std.Build.ResolvedTarget = .{
+        .query = .{},
+        .target = detected.target,
+        .dynamic_linker = detected.dynamic_linker,
+    };
 
     const build_root_directory: std.Build.Cache.Directory = .{
         .path = build_root,
src/Compilation.zig
@@ -30,7 +30,6 @@ const fatal = @import("main.zig").fatal;
 const clangMain = @import("main.zig").clangMain;
 const Module = @import("Module.zig");
 const InternPool = @import("InternPool.zig");
-const BuildId = std.Build.CompileStep.BuildId;
 const Cache = std.Build.Cache;
 const c_codegen = @import("codegen/c.zig");
 const libtsan = @import("libtsan.zig");
@@ -910,7 +909,7 @@ pub const InitOptions = struct {
     linker_print_map: bool = false,
     linker_opt_bisect_limit: i32 = -1,
     each_lib_rpath: ?bool = null,
-    build_id: ?BuildId = null,
+    build_id: ?std.zig.BuildId = null,
     disable_c_depfile: bool = false,
     linker_z_nodelete: bool = false,
     linker_z_notext: bool = false,
src/link.zig
@@ -10,7 +10,6 @@ const wasi_libc = @import("wasi_libc.zig");
 
 const Air = @import("Air.zig");
 const Allocator = std.mem.Allocator;
-const BuildId = std.Build.CompileStep.BuildId;
 const Cache = std.Build.Cache;
 const Compilation = @import("Compilation.zig");
 const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
@@ -185,7 +184,7 @@ pub const Options = struct {
     error_return_tracing: bool,
     skip_linker_dependencies: bool,
     each_lib_rpath: bool,
-    build_id: BuildId,
+    build_id: std.zig.BuildId,
     disable_lld_caching: bool,
     is_test: bool,
     hash_style: HashStyle,
src/main.zig
@@ -21,7 +21,6 @@ const introspect = @import("introspect.zig");
 const EnvVar = introspect.EnvVar;
 const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
 const wasi_libc = @import("wasi_libc.zig");
-const BuildId = std.Build.CompileStep.BuildId;
 const Cache = std.Build.Cache;
 const target_util = @import("target.zig");
 const crash_report = @import("crash_report.zig");
@@ -882,7 +881,7 @@ fn buildOutputType(
     var link_eh_frame_hdr = false;
     var link_emit_relocs = false;
     var each_lib_rpath: ?bool = null;
-    var build_id: ?BuildId = null;
+    var build_id: ?std.zig.BuildId = null;
     var sysroot: ?[]const u8 = null;
     var libc_paths_file: ?[]const u8 = try EnvVar.ZIG_LIBC.get(arena);
     var machine_code_model: std.builtin.CodeModel = .default;
@@ -1551,7 +1550,7 @@ fn buildOutputType(
                         build_id = .fast;
                     } else if (mem.startsWith(u8, arg, "--build-id=")) {
                         const style = arg["--build-id=".len..];
-                        build_id = BuildId.parse(style) catch |err| {
+                        build_id = std.zig.BuildId.parse(style) catch |err| {
                             fatal("unable to parse --build-id style '{s}': {s}", .{
                                 style, @errorName(err),
                             });
@@ -1847,7 +1846,7 @@ fn buildOutputType(
                                     const key = linker_arg[0..equals_pos];
                                     const value = linker_arg[equals_pos + 1 ..];
                                     if (mem.eql(u8, key, "--build-id")) {
-                                        build_id = BuildId.parse(value) catch |err| {
+                                        build_id = std.zig.BuildId.parse(value) catch |err| {
                                             fatal("unable to parse --build-id style '{s}': {s}", .{
                                                 value, @errorName(err),
                                             });
test/behavior/eval.zig
@@ -876,27 +876,27 @@ test "two comptime calls with array default initialized to undefined" {
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
 
     const S = struct {
-        const CrossTarget = struct {
-            dynamic_linker: DynamicLinker = DynamicLinker{},
+        const A = struct {
+            c: B = B{},
 
-            pub fn parse() void {
-                var result: CrossTarget = .{};
-                result.getCpuArch();
+            pub fn d() void {
+                var f: A = .{};
+                f.e();
             }
 
-            pub fn getCpuArch(self: CrossTarget) void {
-                _ = self;
+            pub fn e(g: A) void {
+                _ = g;
             }
         };
 
-        const DynamicLinker = struct {
+        const B = struct {
             buffer: [255]u8 = undefined,
         };
     };
 
     comptime {
-        S.CrossTarget.parse();
-        S.CrossTarget.parse();
+        S.A.d();
+        S.A.d();
     }
 }
 
test/link/bss/build.zig
@@ -7,6 +7,7 @@ pub fn build(b: *std.Build) void {
     const exe = b.addExecutable(.{
         .name = "bss",
         .root_source_file = .{ .path = "main.zig" },
+        .target = b.host,
         .optimize = .Debug,
     });
 
test/link/common_symbols/build.zig
@@ -14,7 +14,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const lib_a = b.addStaticLibrary(.{
         .name = "a",
         .optimize = optimize,
-        .target = .{},
+        .target = b.host,
     });
     lib_a.addCSourceFiles(.{
         .files = &.{ "c.c", "a.c", "b.c" },
test/link/common_symbols_alignment/build.zig
@@ -14,7 +14,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const lib_a = b.addStaticLibrary(.{
         .name = "a",
         .optimize = optimize,
-        .target = .{},
+        .target = b.host,
     });
     lib_a.addCSourceFiles(.{
         .files = &.{"a.c"},
test/link/glibc_compat/build.zig
@@ -8,9 +8,9 @@ pub fn build(b: *std.Build) void {
         const exe = b.addExecutable(.{
             .name = t,
             .root_source_file = .{ .path = "main.c" },
-            .target = std.zig.CrossTarget.parse(
+            .target = b.resolveTargetQuery(std.zig.CrossTarget.parse(
                 .{ .arch_os_abi = t },
-            ) catch unreachable,
+            ) catch unreachable),
         });
         exe.linkLibC();
         // TODO: actually test the output
test/link/interdependent_static_c_libs/build.zig
@@ -14,7 +14,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const lib_a = b.addStaticLibrary(.{
         .name = "a",
         .optimize = optimize,
-        .target = .{},
+        .target = b.host,
     });
     lib_a.addCSourceFile(.{ .file = .{ .path = "a.c" }, .flags = &[_][]const u8{} });
     lib_a.addIncludePath(.{ .path = "." });
@@ -22,7 +22,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const lib_b = b.addStaticLibrary(.{
         .name = "b",
         .optimize = optimize,
-        .target = .{},
+        .target = b.host,
     });
     lib_b.addCSourceFile(.{ .file = .{ .path = "b.c" }, .flags = &[_][]const u8{} });
     lib_b.addIncludePath(.{ .path = "." });
test/link/macho/bugs/13056/build.zig
@@ -14,14 +14,14 @@ pub fn build(b: *std.Build) void {
 }
 
 fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
-    const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable;
-    const sdk = std.zig.system.darwin.getSdk(b.allocator, target_info.target) orelse
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
+    const sdk = std.zig.system.darwin.getSdk(b.allocator, target.target) orelse
         @panic("macOS SDK is required to run the test");
 
     const exe = b.addExecutable(.{
         .name = "test",
         .optimize = optimize,
+        .target = b.host,
     });
     exe.addSystemIncludePath(.{ .path = b.pathJoin(&.{ sdk, "/usr/include" }) });
     exe.addIncludePath(.{ .path = b.pathJoin(&.{ sdk, "/usr/include/c++/v1" }) });
test/link/macho/bugs/13457/build.zig
@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {
 }
 
 fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     const exe = b.addExecutable(.{
         .name = "test",
test/link/macho/bugs/16308/build.zig
@@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void {
     const test_step = b.step("test", "Test it");
     b.default_step = test_step;
 
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     const lib = b.addSharedLibrary(.{
         .name = "a",
test/link/macho/bugs/16628/build.zig
@@ -15,7 +15,7 @@ pub fn build(b: *std.Build) void {
 }
 
 fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     const exe = b.addExecutable(.{
         .name = "test",
test/link/macho/dead_strip/build.zig
@@ -4,7 +4,7 @@ pub const requires_symlinks = true;
 
 pub fn build(b: *std.Build) void {
     const optimize: std.builtin.OptimizeMode = .Debug;
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     const test_step = b.step("test", "Test the program");
     b.default_step = test_step;
@@ -44,7 +44,7 @@ pub fn build(b: *std.Build) void {
 fn createScenario(
     b: *std.Build,
     optimize: std.builtin.OptimizeMode,
-    target: std.zig.CrossTarget,
+    target: std.Build.ResolvedTarget,
     name: []const u8,
 ) *std.Build.Step.Compile {
     const exe = b.addExecutable(.{
test/link/macho/dead_strip_dylibs/build.zig
@@ -52,6 +52,7 @@ fn createScenario(
     const exe = b.addExecutable(.{
         .name = name,
         .optimize = optimize,
+        .target = b.host,
     });
     exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &[0][]const u8{} });
     exe.linkLibC();
test/link/macho/dylib/build.zig
@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {
 }
 
 fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     const dylib = b.addSharedLibrary(.{
         .name = "a",
@@ -55,7 +55,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
 
     check_exe.checkInHeaders();
     check_exe.checkExact("cmd RPATH");
-    check_exe.checkExactPath("path", dylib.getOutputDirectorySource());
+    check_exe.checkExactPath("path", dylib.getEmittedBinDirectory());
     test_step.dependOn(&check_exe.step);
 
     const run = b.addRunArtifact(exe);
test/link/macho/empty/build.zig
@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {
 }
 
 fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     const exe = b.addExecutable(.{
         .name = "test",
test/link/macho/entry/build.zig
@@ -16,7 +16,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const exe = b.addExecutable(.{
         .name = "main",
         .optimize = optimize,
-        .target = .{ .os_tag = .macos },
+        .target = b.resolveTargetQuery(.{ .os_tag = .macos }),
     });
     exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} });
     exe.linkLibC();
test/link/macho/entry_in_archive/build.zig
@@ -16,7 +16,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const lib = b.addStaticLibrary(.{
         .name = "main",
         .optimize = optimize,
-        .target = .{ .os_tag = .macos },
+        .target = b.resolveTargetQuery(.{ .os_tag = .macos }),
     });
     lib.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} });
     lib.linkLibC();
@@ -24,7 +24,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const exe = b.addExecutable(.{
         .name = "main",
         .optimize = optimize,
-        .target = .{ .os_tag = .macos },
+        .target = b.resolveTargetQuery(.{ .os_tag = .macos }),
     });
     exe.linkLibrary(lib);
     exe.linkLibC();
test/link/macho/entry_in_dylib/build.zig
@@ -16,7 +16,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const lib = b.addSharedLibrary(.{
         .name = "bootstrap",
         .optimize = optimize,
-        .target = .{ .os_tag = .macos },
+        .target = b.resolveTargetQuery(.{ .os_tag = .macos }),
     });
     lib.addCSourceFile(.{ .file = .{ .path = "bootstrap.c" }, .flags = &.{} });
     lib.linkLibC();
@@ -25,7 +25,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const exe = b.addExecutable(.{
         .name = "main",
         .optimize = optimize,
-        .target = .{ .os_tag = .macos },
+        .target = b.resolveTargetQuery(.{ .os_tag = .macos }),
     });
     exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} });
     exe.linkLibrary(lib);
test/link/macho/headerpad/build.zig
@@ -112,6 +112,7 @@ fn simpleExe(
     const exe = b.addExecutable(.{
         .name = name,
         .optimize = optimize,
+        .target = b.host,
     });
     exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} });
     exe.linkLibC();
test/link/macho/linksection/build.zig
@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {
 }
 
 fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
-    const target = std.zig.CrossTarget{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     const obj = b.addObject(.{
         .name = "test",
test/link/macho/needed_framework/build.zig
@@ -19,6 +19,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const exe = b.addExecutable(.{
         .name = "test",
         .optimize = optimize,
+        .target = b.host,
     });
     exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &[0][]const u8{} });
     exe.linkLibC();
test/link/macho/needed_library/build.zig
@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {
 }
 
 fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     const dylib = b.addSharedLibrary(.{
         .name = "a",
@@ -33,7 +33,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     });
     exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &[0][]const u8{} });
     exe.linkLibC();
-    exe.linkSystemLibraryNeeded("a");
+    exe.root_module.linkSystemLibrary("a", .{ .needed = true });
     exe.addLibraryPath(dylib.getEmittedBinDirectory());
     exe.addRPath(dylib.getEmittedBinDirectory());
     exe.dead_strip_dylibs = true;
test/link/macho/objc/build.zig
@@ -17,6 +17,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const exe = b.addExecutable(.{
         .name = "test",
         .optimize = optimize,
+        .target = b.host,
     });
     exe.addIncludePath(.{ .path = "." });
     exe.addCSourceFile(.{ .file = .{ .path = "Foo.m" }, .flags = &[0][]const u8{} });
test/link/macho/objcpp/build.zig
@@ -17,6 +17,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const exe = b.addExecutable(.{
         .name = "test",
         .optimize = optimize,
+        .target = b.host,
     });
     b.default_step.dependOn(&exe.step);
     exe.addIncludePath(.{ .path = "." });
test/link/macho/pagezero/build.zig
@@ -7,7 +7,7 @@ pub fn build(b: *std.Build) void {
     b.default_step = test_step;
 
     const optimize: std.builtin.OptimizeMode = .Debug;
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     {
         const exe = b.addExecutable(.{
test/link/macho/reexports/build.zig
@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {
 }
 
 fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     const lib = b.addStaticLibrary(.{
         .name = "a",
test/link/macho/search_strategy/build.zig
@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {
 }
 
 fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     {
         // -search_dylibs_first
@@ -45,9 +45,9 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
 fn createScenario(
     b: *std.Build,
     optimize: std.builtin.OptimizeMode,
-    target: std.zig.CrossTarget,
+    target: std.Build.ResolvedTarget,
     name: []const u8,
-    search_strategy: std.Build.Step.Compile.SystemLib.SearchStrategy,
+    search_strategy: std.Build.Module.SystemLib.SearchStrategy,
 ) *std.Build.Step.Compile {
     const static = b.addStaticLibrary(.{
         .name = name,
test/link/macho/stack_size/build.zig
@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {
 }
 
 fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     const exe = b.addExecutable(.{
         .name = "main",
test/link/macho/strict_validation/build.zig
@@ -14,7 +14,7 @@ pub fn build(b: *std.Build) void {
 }
 
 fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     const exe = b.addExecutable(.{
         .name = "main",
test/link/macho/tbdv3/build.zig
@@ -15,7 +15,7 @@ pub fn build(b: *std.Build) void {
 }
 
 fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     const lib = b.addSharedLibrary(.{
         .name = "a",
test/link/macho/tls/build.zig
@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {
 }
 
 fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     const lib = b.addSharedLibrary(.{
         .name = "a",
test/link/macho/unwind_info/build.zig
@@ -14,7 +14,7 @@ pub fn build(b: *std.Build) void {
 }
 
 fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     testUnwindInfo(b, test_step, optimize, target, false, "no-dead-strip");
     testUnwindInfo(b, test_step, optimize, target, true, "yes-dead-strip");
@@ -24,7 +24,7 @@ fn testUnwindInfo(
     b: *std.Build,
     test_step: *std.Build.Step,
     optimize: std.builtin.OptimizeMode,
-    target: std.zig.CrossTarget,
+    target: std.Build.ResolvedTarget,
     dead_strip: bool,
     name: []const u8,
 ) void {
@@ -66,7 +66,7 @@ fn testUnwindInfo(
 fn createScenario(
     b: *std.Build,
     optimize: std.builtin.OptimizeMode,
-    target: std.zig.CrossTarget,
+    target: std.Build.ResolvedTarget,
     name: []const u8,
 ) *std.Build.Step.Compile {
     const exe = b.addExecutable(.{
test/link/macho/weak_framework/build.zig
@@ -17,6 +17,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const exe = b.addExecutable(.{
         .name = "test",
         .optimize = optimize,
+        .target = b.host,
     });
     exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &[0][]const u8{} });
     exe.linkLibC();
test/link/macho/weak_library/build.zig
@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {
 }
 
 fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const target = b.resolveTargetQuery(.{ .os_tag = .macos });
 
     const dylib = b.addSharedLibrary(.{
         .name = "a",
@@ -32,7 +32,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     });
     exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &[0][]const u8{} });
     exe.linkLibC();
-    exe.linkSystemLibraryWeak("a");
+    exe.root_module.linkSystemLibrary("a", .{ .weak = true });
     exe.addLibraryPath(dylib.getEmittedBinDirectory());
     exe.addRPath(dylib.getEmittedBinDirectory());
 
test/link/static_libs_from_object_files/build.zig
@@ -59,7 +59,7 @@ fn add(b: *Build, test_step: *Step, files: []const LazyPath, optimize: std.built
             .name = "test1",
             .root_source_file = .{ .path = "main.zig" },
             .optimize = optimize,
-            .target = .{},
+            .target = b.host,
         });
 
         for (files) |file| {
@@ -77,12 +77,12 @@ fn add(b: *Build, test_step: *Step, files: []const LazyPath, optimize: std.built
     {
         const lib_a = b.addStaticLibrary(.{
             .name = "test2_a",
-            .target = .{},
+            .target = b.host,
             .optimize = optimize,
         });
         const lib_b = b.addStaticLibrary(.{
             .name = "test2_b",
-            .target = .{},
+            .target = b.host,
             .optimize = optimize,
         });
 
@@ -94,6 +94,7 @@ fn add(b: *Build, test_step: *Step, files: []const LazyPath, optimize: std.built
         const exe = b.addExecutable(.{
             .name = "test2",
             .root_source_file = .{ .path = "main.zig" },
+            .target = b.host,
             .optimize = optimize,
         });
         exe.linkLibrary(lib_a);
@@ -110,19 +111,19 @@ fn add(b: *Build, test_step: *Step, files: []const LazyPath, optimize: std.built
     {
         const lib_a = b.addStaticLibrary(.{
             .name = "test3_a",
-            .target = .{},
+            .target = b.host,
             .optimize = optimize,
         });
         const lib_b = b.addStaticLibrary(.{
             .name = "test3_b",
-            .target = .{},
+            .target = b.host,
             .optimize = optimize,
         });
 
         for (files, 1..) |file, i| {
             const obj = b.addObject(.{
                 .name = b.fmt("obj_{}", .{i}),
-                .target = .{},
+                .target = b.host,
                 .optimize = optimize,
             });
             obj.addCSourceFile(.{ .file = file, .flags = &flags });
@@ -134,6 +135,7 @@ fn add(b: *Build, test_step: *Step, files: []const LazyPath, optimize: std.built
         const exe = b.addExecutable(.{
             .name = "test3",
             .root_source_file = .{ .path = "main.zig" },
+            .target = b.host,
             .optimize = optimize,
         });
         exe.linkLibrary(lib_a);
test/link/wasm/archive/build.zig
@@ -20,11 +20,11 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
         .root_source_file = .{ .path = "main.zig" },
         .optimize = optimize,
         .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
+        .strip = false,
     });
     lib.entry = .disabled;
     lib.use_llvm = false;
     lib.use_lld = false;
-    lib.strip = false;
 
     const check = lib.checkObject();
     check.checkInHeaders();
test/link/wasm/basic-features/build.zig
@@ -8,12 +8,12 @@ pub fn build(b: *std.Build) void {
         .name = "lib",
         .root_source_file = .{ .path = "main.zig" },
         .optimize = .Debug,
-        .target = .{
+        .target = b.resolveTargetQuery(.{
             .cpu_arch = .wasm32,
             .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
             .cpu_features_add = std.Target.wasm.featureSet(&.{.atomics}),
             .os_tag = .freestanding,
-        },
+        }),
     });
     lib.entry = .disabled;
     lib.use_llvm = false;
test/link/wasm/bss/build.zig
@@ -17,13 +17,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt
         const lib = b.addExecutable(.{
             .name = "lib",
             .root_source_file = .{ .path = "lib.zig" },
-            .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
+            .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
             .optimize = optimize_mode,
+            .strip = false,
         });
         lib.entry = .disabled;
         lib.use_llvm = false;
         lib.use_lld = false;
-        lib.strip = false;
         // to make sure the bss segment is emitted, we must import memory
         lib.import_memory = true;
         lib.link_gc_sections = false;
@@ -65,13 +65,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt
         const lib = b.addExecutable(.{
             .name = "lib",
             .root_source_file = .{ .path = "lib2.zig" },
-            .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
+            .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
             .optimize = optimize_mode,
+            .strip = false,
         });
         lib.entry = .disabled;
         lib.use_llvm = false;
         lib.use_lld = false;
-        lib.strip = false;
         // to make sure the bss segment is emitted, we must import memory
         lib.import_memory = true;
         lib.link_gc_sections = false;
test/link/wasm/export/build.zig
@@ -17,7 +17,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
         .name = "no-export",
         .root_source_file = .{ .path = "main.zig" },
         .optimize = optimize,
-        .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
+        .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
     });
     no_export.entry = .disabled;
     no_export.use_llvm = false;
@@ -27,7 +27,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
         .name = "dynamic",
         .root_source_file = .{ .path = "main.zig" },
         .optimize = optimize,
-        .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
+        .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
     });
     dynamic_export.entry = .disabled;
     dynamic_export.rdynamic = true;
@@ -38,10 +38,10 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
         .name = "force",
         .root_source_file = .{ .path = "main.zig" },
         .optimize = optimize,
-        .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
+        .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
     });
     force_export.entry = .disabled;
-    force_export.export_symbol_names = &.{"foo"};
+    force_export.root_module.export_symbol_names = &.{"foo"};
     force_export.use_llvm = false;
     force_export.use_lld = false;
 
test/link/wasm/export-data/build.zig
@@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void {
     });
     lib.entry = .disabled;
     lib.use_lld = false;
-    lib.export_symbol_names = &.{ "foo", "bar" };
+    lib.root_module.export_symbol_names = &.{ "foo", "bar" };
     lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse
 
     const check_lib = lib.checkObject();
test/link/wasm/extern/build.zig
@@ -17,7 +17,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
         .name = "extern",
         .root_source_file = .{ .path = "main.zig" },
         .optimize = optimize,
-        .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi },
+        .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .wasi }),
     });
     exe.addCSourceFile(.{ .file = .{ .path = "foo.c" }, .flags = &.{} });
     exe.use_llvm = false;
test/link/wasm/extern-mangle/build.zig
@@ -14,7 +14,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const lib = b.addExecutable(.{
         .name = "lib",
         .root_source_file = .{ .path = "lib.zig" },
-        .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
+        .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
         .optimize = optimize,
     });
     lib.entry = .disabled;
test/link/wasm/function-table/build.zig
@@ -16,7 +16,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const import_table = b.addExecutable(.{
         .name = "import_table",
         .root_source_file = .{ .path = "lib.zig" },
-        .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
+        .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
         .optimize = optimize,
     });
     import_table.entry = .disabled;
@@ -28,7 +28,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const export_table = b.addExecutable(.{
         .name = "export_table",
         .root_source_file = .{ .path = "lib.zig" },
-        .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
+        .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
         .optimize = optimize,
     });
     export_table.entry = .disabled;
@@ -40,7 +40,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const regular_table = b.addExecutable(.{
         .name = "regular_table",
         .root_source_file = .{ .path = "lib.zig" },
-        .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
+        .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
         .optimize = optimize,
     });
     regular_table.entry = .disabled;
test/link/wasm/infer-features/build.zig
@@ -7,11 +7,11 @@ pub fn build(b: *std.Build) void {
     const c_obj = b.addObject(.{
         .name = "c_obj",
         .optimize = .Debug,
-        .target = .{
+        .target = b.resolveTargetQuery(.{
             .cpu_arch = .wasm32,
             .cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge },
             .os_tag = .freestanding,
-        },
+        }),
     });
     c_obj.addCSourceFile(.{ .file = .{ .path = "foo.c" }, .flags = &.{} });
 
@@ -21,11 +21,11 @@ pub fn build(b: *std.Build) void {
         .name = "lib",
         .root_source_file = .{ .path = "main.zig" },
         .optimize = .Debug,
-        .target = .{
+        .target = b.resolveTargetQuery(.{
             .cpu_arch = .wasm32,
             .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
             .os_tag = .freestanding,
-        },
+        }),
     });
     lib.entry = .disabled;
     lib.use_llvm = false;
test/link/wasm/producers/build.zig
@@ -17,13 +17,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const lib = b.addExecutable(.{
         .name = "lib",
         .root_source_file = .{ .path = "lib.zig" },
-        .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
+        .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
         .optimize = optimize,
+        .strip = false,
     });
     lib.entry = .disabled;
     lib.use_llvm = false;
     lib.use_lld = false;
-    lib.strip = false;
     b.installArtifact(lib);
 
     const version_fmt = "version " ++ builtin.zig_version_string;
test/link/wasm/segments/build.zig
@@ -16,13 +16,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const lib = b.addExecutable(.{
         .name = "lib",
         .root_source_file = .{ .path = "lib.zig" },
-        .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
+        .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
         .optimize = optimize,
+        .strip = false,
     });
     lib.entry = .disabled;
     lib.use_llvm = false;
     lib.use_lld = false;
-    lib.strip = false;
     lib.link_gc_sections = false; // so data is not garbage collected and we can verify data section
     b.installArtifact(lib);
 
test/link/wasm/shared-memory/build.zig
@@ -21,16 +21,16 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt
             .os_tag = .freestanding,
         },
         .optimize = optimize_mode,
+        .strip = false,
+        .single_threaded = false,
     });
     lib.entry = .disabled;
     lib.use_lld = false;
-    lib.strip = false;
     lib.import_memory = true;
     lib.export_memory = true;
     lib.shared_memory = true;
     lib.max_memory = 67108864;
-    lib.single_threaded = false;
-    lib.export_symbol_names = &.{"foo"};
+    lib.root_module.export_symbol_names = &.{"foo"};
 
     const check_lib = lib.checkObject();
 
test/link/wasm/stack_pointer/build.zig
@@ -16,13 +16,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const lib = b.addExecutable(.{
         .name = "lib",
         .root_source_file = .{ .path = "lib.zig" },
-        .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
+        .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
         .optimize = optimize,
+        .strip = false,
     });
     lib.entry = .disabled;
     lib.use_llvm = false;
     lib.use_lld = false;
-    lib.strip = false;
     lib.stack_size = std.wasm.page_size * 2; // set an explicit stack size
     lib.link_gc_sections = false;
     b.installArtifact(lib);
test/link/wasm/type/build.zig
@@ -16,13 +16,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const lib = b.addExecutable(.{
         .name = "lib",
         .root_source_file = .{ .path = "lib.zig" },
-        .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
+        .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
         .optimize = optimize,
+        .strip = false,
     });
     lib.entry = .disabled;
     lib.use_llvm = false;
     lib.use_lld = false;
-    lib.strip = false;
     b.installArtifact(lib);
 
     const check_lib = lib.checkObject();
test/link/elf.zig
@@ -5,20 +5,20 @@
 pub fn testAll(b: *Build) *Step {
     const elf_step = b.step("test-elf", "Run ELF tests");
 
-    const default_target = CrossTarget{
+    const default_target = b.resolveTargetQuery(.{
         .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs
         .os_tag = .linux,
-    };
-    const musl_target = CrossTarget{
+    });
+    const musl_target = b.resolveTargetQuery(.{
         .cpu_arch = .x86_64,
         .os_tag = .linux,
         .abi = .musl,
-    };
-    const glibc_target = CrossTarget{
+    });
+    const glibc_target = b.resolveTargetQuery(.{
         .cpu_arch = .x86_64,
         .os_tag = .linux,
         .abi = .gnu,
-    };
+    });
 
     // Exercise linker in -r mode
     elf_step.dependOn(testEmitRelocatable(b, .{ .use_llvm = false, .target = musl_target }));
@@ -132,14 +132,18 @@ pub fn testAll(b: *Build) *Step {
 fn testAbsSymbols(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "abs-symbols", opts);
 
-    const obj = addObject(b, "obj", opts);
-    addAsmSourceBytes(obj,
+    const obj = addObject(b, opts, .{
+        .name = "obj",
+        .asm_source_bytes =
         \\.globl foo
         \\foo = 0x800008
-    );
+        \\
+        ,
+    });
 
-    const exe = addExecutable(b, "test", opts);
-    addCSourceBytes(exe,
+    const exe = addExecutable(b, opts, .{
+        .name = "test",
+        .c_source_bytes =
         \\#include <signal.h>
         \\#include <stdio.h>
         \\#include <stdlib.h>
@@ -159,7 +163,8 @@ fn testAbsSymbols(b: *Build, opts: Options) *Step {
         \\  foo = 5;
         \\  return 0;
         \\}
-    , &.{});
+        ,
+    });
     exe.addObject(obj);
     exe.linkLibC();
 
@@ -173,31 +178,36 @@ fn testAbsSymbols(b: *Build, opts: Options) *Step {
 fn testAsNeeded(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "as-needed", opts);
 
-    const main_o = addObject(b, "main", opts);
-    addCSourceBytes(main_o,
+    const main_o = addObject(b, opts, .{
+        .name = "main",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\int baz();
         \\int main() {
         \\  printf("%d\n", baz());
         \\  return 0;
         \\}
-    , &.{});
+        \\
+        ,
+    });
     main_o.linkLibC();
 
-    const libfoo = addSharedLibrary(b, "foo", opts);
+    const libfoo = addSharedLibrary(b, opts, .{ .name = "foo" });
     addCSourceBytes(libfoo, "int foo() { return 42; }", &.{});
 
-    const libbar = addSharedLibrary(b, "bar", opts);
+    const libbar = addSharedLibrary(b, opts, .{ .name = "bar" });
     addCSourceBytes(libbar, "int bar() { return 42; }", &.{});
 
-    const libbaz = addSharedLibrary(b, "baz", opts);
+    const libbaz = addSharedLibrary(b, opts, .{ .name = "baz" });
     addCSourceBytes(libbaz,
         \\int foo();
         \\int baz() { return foo(); }
     , &.{});
 
     {
-        const exe = addExecutable(b, "test", opts);
+        const exe = addExecutable(b, opts, .{
+            .name = "test",
+        });
         exe.addObject(main_o);
         exe.linkSystemLibrary2("foo", .{ .needed = true });
         exe.addLibraryPath(libfoo.getEmittedBinDirectory());
@@ -225,7 +235,9 @@ fn testAsNeeded(b: *Build, opts: Options) *Step {
     }
 
     {
-        const exe = addExecutable(b, "test", opts);
+        const exe = addExecutable(b, opts, .{
+            .name = "test",
+        });
         exe.addObject(main_o);
         exe.linkSystemLibrary2("foo", .{ .needed = false });
         exe.addLibraryPath(libfoo.getEmittedBinDirectory());
@@ -259,7 +271,7 @@ fn testAsNeeded(b: *Build, opts: Options) *Step {
 fn testCanonicalPlt(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "canonical-plt", opts);
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(dso,
         \\void *foo() {
         \\  return foo;
@@ -269,17 +281,21 @@ fn testCanonicalPlt(b: *Build, opts: Options) *Step {
         \\}
     , &.{});
 
-    const b_o = addObject(b, "obj", opts);
-    addCSourceBytes(b_o,
+    const b_o = addObject(b, opts, .{
+        .name = "obj",
+        .c_source_bytes =
         \\void *bar();
         \\void *baz() {
         \\  return bar;
         \\}
-    , &.{});
-    b_o.force_pic = true;
+        \\
+        ,
+        .pic = true,
+    });
 
-    const main_o = addObject(b, "main", opts);
-    addCSourceBytes(main_o,
+    const main_o = addObject(b, opts, .{
+        .name = "main",
+        .c_source_bytes =
         \\#include <assert.h>
         \\void *foo();
         \\void *bar();
@@ -290,11 +306,15 @@ fn testCanonicalPlt(b: *Build, opts: Options) *Step {
         \\  assert(bar == baz());
         \\  return 0;
         \\}
-    , &.{});
+        \\
+        ,
+        .pic = false,
+    });
     main_o.linkLibC();
-    main_o.force_pic = false;
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{
+        .name = "main",
+    });
     exe.addObject(main_o);
     exe.addObject(b_o);
     exe.linkLibrary(dso);
@@ -311,7 +331,9 @@ fn testCanonicalPlt(b: *Build, opts: Options) *Step {
 fn testCommonSymbols(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "common-symbols", opts);
 
-    const exe = addExecutable(b, "test", opts);
+    const exe = addExecutable(b, opts, .{
+        .name = "test",
+    });
     addCSourceBytes(exe,
         \\int foo;
         \\int bar;
@@ -338,8 +360,9 @@ fn testCommonSymbols(b: *Build, opts: Options) *Step {
 fn testCommonSymbolsInArchive(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "common-symbols-in-archive", opts);
 
-    const a_o = addObject(b, "a", opts);
-    addCSourceBytes(a_o,
+    const a_o = addObject(b, opts, .{
+        .name = "a",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\int foo;
         \\int bar;
@@ -348,28 +371,43 @@ fn testCommonSymbolsInArchive(b: *Build, opts: Options) *Step {
         \\int main() {
         \\  printf("%d %d %d %d\n", foo, bar, baz, two ? two() : -1);
         \\}
-    , &.{"-fcommon"});
+        \\
+        ,
+        .c_source_flags = &.{"-fcommon"},
+    });
     a_o.linkLibC();
 
-    const b_o = addObject(b, "b", opts);
-    addCSourceBytes(b_o, "int foo = 5;", &.{"-fcommon"});
+    const b_o = addObject(b, opts, .{
+        .name = "b",
+        .c_source_bytes = "int foo = 5;",
+        .c_source_flags = &.{"-fcommon"},
+    });
 
     {
-        const c_o = addObject(b, "c", opts);
-        addCSourceBytes(c_o,
+        const c_o = addObject(b, opts, .{
+            .name = "c",
+            .c_source_bytes =
             \\int bar;
             \\int two() { return 2; }
-        , &.{"-fcommon"});
-
-        const d_o = addObject(b, "d", opts);
-        addCSourceBytes(d_o, "int baz;", &.{"-fcommon"});
-
-        const lib = addStaticLibrary(b, "lib", opts);
+            \\
+            ,
+            .c_source_flags = &.{"-fcommon"},
+        });
+
+        const d_o = addObject(b, opts, .{
+            .name = "d",
+            .c_source_bytes = "int baz;",
+            .c_source_flags = &.{"-fcommon"},
+        });
+
+        const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
         lib.addObject(b_o);
         lib.addObject(c_o);
         lib.addObject(d_o);
 
-        const exe = addExecutable(b, "test", opts);
+        const exe = addExecutable(b, opts, .{
+            .name = "test",
+        });
         exe.addObject(a_o);
         exe.linkLibrary(lib);
         exe.linkLibC();
@@ -380,18 +418,23 @@ fn testCommonSymbolsInArchive(b: *Build, opts: Options) *Step {
     }
 
     {
-        const e_o = addObject(b, "e", opts);
-        addCSourceBytes(e_o,
+        const e_o = addObject(b, opts, .{
+            .name = "e",
+            .c_source_bytes =
             \\int bar = 0;
             \\int baz = 7;
             \\int two() { return 2; }
-        , &.{"-fcommon"});
+            ,
+            .c_source_flags = &.{"-fcommon"},
+        });
 
-        const lib = addStaticLibrary(b, "lib", opts);
+        const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
         lib.addObject(b_o);
         lib.addObject(e_o);
 
-        const exe = addExecutable(b, "test", opts);
+        const exe = addExecutable(b, opts, .{
+            .name = "test",
+        });
         exe.addObject(a_o);
         exe.linkLibrary(lib);
         exe.linkLibC();
@@ -407,21 +450,23 @@ fn testCommonSymbolsInArchive(b: *Build, opts: Options) *Step {
 fn testCopyrel(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "copyrel", opts);
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(dso,
         \\int foo = 3;
         \\int bar = 5;
     , &.{});
 
-    const exe = addExecutable(b, "main", opts);
-    addCSourceBytes(exe,
+    const exe = addExecutable(b, opts, .{
+        .name = "main",
+        .c_source_bytes =
         \\#include<stdio.h>
         \\extern int foo, bar;
         \\int main() {
         \\  printf("%d %d\n", foo, bar);
         \\  return 0;
         \\}
-    , &.{});
+        ,
+    });
     exe.linkLibrary(dso);
     exe.linkLibC();
     // https://github.com/ziglang/zig/issues/17619
@@ -437,7 +482,7 @@ fn testCopyrel(b: *Build, opts: Options) *Step {
 fn testCopyrelAlias(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "copyrel-alias", opts);
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(dso,
         \\int bruh = 31;
         \\int foo = 42;
@@ -445,7 +490,10 @@ fn testCopyrelAlias(b: *Build, opts: Options) *Step {
         \\extern int baz __attribute__((alias("foo")));
     , &.{});
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{
+        .name = "main",
+        .pic = false,
+    });
     addCSourceBytes(exe,
         \\#include<stdio.h>
         \\extern int foo;
@@ -461,7 +509,6 @@ fn testCopyrelAlias(b: *Build, opts: Options) *Step {
     , &.{});
     exe.linkLibrary(dso);
     exe.linkLibC();
-    exe.force_pic = false;
     exe.pie = false;
 
     const run = addRunArtifact(exe);
@@ -474,28 +521,31 @@ fn testCopyrelAlias(b: *Build, opts: Options) *Step {
 fn testCopyrelAlignment(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "copyrel-alignment", opts);
 
-    const a_so = addSharedLibrary(b, "a", opts);
+    const a_so = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(a_so, "__attribute__((aligned(32))) int foo = 5;", &.{});
 
-    const b_so = addSharedLibrary(b, "b", opts);
+    const b_so = addSharedLibrary(b, opts, .{ .name = "b" });
     addCSourceBytes(b_so, "__attribute__((aligned(8))) int foo = 5;", &.{});
 
-    const c_so = addSharedLibrary(b, "c", opts);
+    const c_so = addSharedLibrary(b, opts, .{ .name = "c" });
     addCSourceBytes(c_so, "__attribute__((aligned(256))) int foo = 5;", &.{});
 
-    const obj = addObject(b, "main", opts);
-    addCSourceBytes(obj,
+    const obj = addObject(b, opts, .{
+        .name = "main",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\extern int foo;
         \\int main() { printf("%d\n", foo); }
-    , &.{});
+        \\
+        ,
+        .pic = false,
+    });
     obj.linkLibC();
-    obj.force_pic = false;
 
     const exp_stdout = "5\n";
 
     {
-        const exe = addExecutable(b, "main", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main" });
         exe.addObject(obj);
         exe.linkLibrary(a_so);
         exe.linkLibC();
@@ -514,7 +564,7 @@ fn testCopyrelAlignment(b: *Build, opts: Options) *Step {
     }
 
     {
-        const exe = addExecutable(b, "main", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main" });
         exe.addObject(obj);
         exe.linkLibrary(b_so);
         exe.linkLibC();
@@ -533,7 +583,7 @@ fn testCopyrelAlignment(b: *Build, opts: Options) *Step {
     }
 
     {
-        const exe = addExecutable(b, "main", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main" });
         exe.addObject(obj);
         exe.linkLibrary(c_so);
         exe.linkLibC();
@@ -557,7 +607,7 @@ fn testCopyrelAlignment(b: *Build, opts: Options) *Step {
 fn testDsoPlt(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "dso-plt", opts);
 
-    const dso = addSharedLibrary(b, "dso", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "dso" });
     addCSourceBytes(dso,
         \\#include<stdio.h>
         \\void world() {
@@ -573,7 +623,7 @@ fn testDsoPlt(b: *Build, opts: Options) *Step {
     , &.{});
     dso.linkLibC();
 
-    const exe = addExecutable(b, "test", opts);
+    const exe = addExecutable(b, opts, .{ .name = "test" });
     addCSourceBytes(exe,
         \\#include<stdio.h>
         \\void world() {
@@ -599,7 +649,7 @@ fn testDsoPlt(b: *Build, opts: Options) *Step {
 fn testDsoUndef(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "dso-undef", opts);
 
-    const dso = addSharedLibrary(b, "dso", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "dso" });
     addCSourceBytes(dso,
         \\extern int foo;
         \\int bar = 5;
@@ -607,13 +657,15 @@ fn testDsoUndef(b: *Build, opts: Options) *Step {
     , &.{});
     dso.linkLibC();
 
-    const obj = addObject(b, "obj", opts);
-    addCSourceBytes(obj, "int foo = 3;", &.{});
+    const obj = addObject(b, opts, .{
+        .name = "obj",
+        .c_source_bytes = "int foo = 3;",
+    });
 
-    const lib = addStaticLibrary(b, "lib", opts);
+    const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
     lib.addObject(obj);
 
-    const exe = addExecutable(b, "test", opts);
+    const exe = addExecutable(b, opts, .{ .name = "test" });
     exe.linkLibrary(dso);
     exe.linkLibrary(lib);
     addCSourceBytes(exe,
@@ -641,8 +693,9 @@ fn testDsoUndef(b: *Build, opts: Options) *Step {
 fn testEmitRelocatable(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "emit-relocatable", opts);
 
-    const obj1 = addObject(b, "obj1", opts);
-    addZigSourceBytes(obj1,
+    const obj1 = addObject(b, opts, .{
+        .name = "obj1",
+        .zig_source_bytes =
         \\const std = @import("std");
         \\extern var bar: i32;
         \\export fn foo() i32 {
@@ -651,18 +704,20 @@ fn testEmitRelocatable(b: *Build, opts: Options) *Step {
         \\export fn printFoo() void {
         \\    std.debug.print("foo={d}\n", .{foo()});
         \\}
-    );
-    addCSourceBytes(obj1,
+        ,
+        .c_source_bytes =
         \\#include <stdio.h>
         \\int bar = 42;
         \\void printBar() {
         \\  fprintf(stderr, "bar=%d\n", bar);
         \\}
-    , &.{});
+        ,
+    });
     obj1.linkLibC();
 
-    const exe = addExecutable(b, "test", opts);
-    addZigSourceBytes(exe,
+    const exe = addExecutable(b, opts, .{
+        .name = "test",
+        .zig_source_bytes =
         \\const std = @import("std");
         \\extern fn printFoo() void;
         \\extern fn printBar() void;
@@ -670,7 +725,8 @@ fn testEmitRelocatable(b: *Build, opts: Options) *Step {
         \\    printFoo();
         \\    printBar();
         \\}
-    );
+        ,
+    });
     exe.addObject(obj1);
     exe.linkLibC();
 
@@ -688,20 +744,26 @@ fn testEmitRelocatable(b: *Build, opts: Options) *Step {
 fn testEmitStaticLib(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "emit-static-lib", opts);
 
-    const obj1 = addObject(b, "obj1", opts);
-    addCSourceBytes(obj1,
+    const obj1 = addObject(b, opts, .{
+        .name = "obj1",
+        .c_source_bytes =
         \\int foo = 0;
         \\int bar = 2;
         \\int fooBar() {
         \\  return foo + bar;
         \\}
-    , &.{});
+        ,
+    });
 
-    const obj2 = addObject(b, "obj2", opts);
-    addCSourceBytes(obj2, "int tentative;", &.{"-fcommon"});
+    const obj2 = addObject(b, opts, .{
+        .name = "obj2",
+        .c_source_bytes = "int tentative;",
+        .c_source_flags = &.{"-fcommon"},
+    });
 
-    const obj3 = addObject(b, "a_very_long_file_name_so_that_it_ends_up_in_strtab", opts);
-    addZigSourceBytes(obj3,
+    const obj3 = addObject(b, opts, .{
+        .name = "a_very_long_file_name_so_that_it_ends_up_in_strtab",
+        .zig_source_bytes =
         \\fn weakFoo() callconv(.C) usize {
         \\    return 42;
         \\}
@@ -710,9 +772,10 @@ fn testEmitStaticLib(b: *Build, opts: Options) *Step {
         \\    @export(weakFoo, .{ .name = "weakFoo", .linkage = .Weak });
         \\    @export(strongBar, .{ .name = "strongBarAlias", .linkage = .Strong });
         \\}
-    );
+        ,
+    });
 
-    const lib = addStaticLibrary(b, "lib", opts);
+    const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
     lib.addObject(obj1);
     lib.addObject(obj2);
     lib.addObject(obj3);
@@ -747,30 +810,36 @@ fn testEmitStaticLib(b: *Build, opts: Options) *Step {
 fn testEmitStaticLibZig(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "emit-static-lib-zig", opts);
 
-    const obj1 = addObject(b, "obj1", opts);
-    addZigSourceBytes(obj1,
+    const obj1 = addObject(b, opts, .{
+        .name = "obj1",
+        .zig_source_bytes =
         \\export var foo: i32 = 42;
         \\export var bar: i32 = 2;
-    );
+        ,
+    });
 
-    const lib = addStaticLibrary(b, "lib", opts);
-    addZigSourceBytes(lib,
+    const lib = addStaticLibrary(b, opts, .{
+        .name = "lib",
+        .zig_source_bytes =
         \\extern var foo: i32;
         \\extern var bar: i32;
         \\export fn fooBar() i32 {
         \\  return foo + bar;
         \\}
-    );
+        ,
+    });
     lib.addObject(obj1);
 
-    const exe = addExecutable(b, "test", opts);
-    addZigSourceBytes(exe,
+    const exe = addExecutable(b, opts, .{
+        .name = "test",
+        .zig_source_bytes =
         \\const std = @import("std");
         \\extern fn fooBar() i32;
         \\pub fn main() void {
         \\  std.debug.print("{d}", .{fooBar()});
         \\}
-    );
+        ,
+    });
     exe.linkLibrary(lib);
 
     const run = addRunArtifact(exe);
@@ -783,7 +852,7 @@ fn testEmitStaticLibZig(b: *Build, opts: Options) *Step {
 fn testEmptyObject(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "empty-object", opts);
 
-    const exe = addExecutable(b, "test", opts);
+    const exe = addExecutable(b, opts, .{ .name = "test" });
     addCSourceBytes(exe, "int main() { return 0; }", &.{});
     addCSourceBytes(exe, "", &.{});
     exe.linkLibC();
@@ -798,18 +867,23 @@ fn testEmptyObject(b: *Build, opts: Options) *Step {
 fn testEntryPoint(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "entry-point", opts);
 
-    const a_o = addObject(b, "a", opts);
-    addAsmSourceBytes(a_o,
+    const a_o = addObject(b, opts, .{
+        .name = "a",
+        .asm_source_bytes =
         \\.globl foo, bar
         \\foo = 0x1000
         \\bar = 0x2000
-    );
+        \\
+        ,
+    });
 
-    const b_o = addObject(b, "b", opts);
-    addCSourceBytes(b_o, "int main() { return 0; }", &.{});
+    const b_o = addObject(b, opts, .{
+        .name = "b",
+        .c_source_bytes = "int main() { return 0; }",
+    });
 
     {
-        const exe = addExecutable(b, "main", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main" });
         exe.addObject(a_o);
         exe.addObject(b_o);
         exe.entry = .{ .symbol_name = "foo" };
@@ -825,7 +899,7 @@ fn testEntryPoint(b: *Build, opts: Options) *Step {
         // TODO looks like not assigning a unique name to this executable will
         // cause an artifact collision taking the cached executable from the above
         // step instead of generating a new one.
-        const exe = addExecutable(b, "other", opts);
+        const exe = addExecutable(b, opts, .{ .name = "other" });
         exe.addObject(a_o);
         exe.addObject(b_o);
         exe.entry = .{ .symbol_name = "bar" };
@@ -843,8 +917,9 @@ fn testEntryPoint(b: *Build, opts: Options) *Step {
 fn testExportDynamic(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "export-dynamic", opts);
 
-    const obj = addObject(b, "obj", opts);
-    addAsmSourceBytes(obj,
+    const obj = addObject(b, opts, .{
+        .name = "obj",
+        .asm_source_bytes =
         \\.text
         \\  .globl foo
         \\  .hidden foo
@@ -856,12 +931,14 @@ fn testExportDynamic(b: *Build, opts: Options) *Step {
         \\  .globl _start
         \\_start:
         \\  nop
-    );
+        \\
+        ,
+    });
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(dso, "int baz = 10;", &.{});
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\extern int baz;
         \\int callBaz() {
@@ -885,7 +962,7 @@ fn testExportDynamic(b: *Build, opts: Options) *Step {
 fn testExportSymbolsFromExe(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "export-symbols-from-exe", opts);
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(dso,
         \\void expfn1();
         \\void expfn2() {}
@@ -895,7 +972,7 @@ fn testExportSymbolsFromExe(b: *Build, opts: Options) *Step {
         \\}
     , &.{});
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\void expfn1() {}
         \\void expfn2() {}
@@ -923,10 +1000,10 @@ fn testExportSymbolsFromExe(b: *Build, opts: Options) *Step {
 fn testFuncAddress(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "func-address", opts);
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(dso, "void fn() {}", &.{});
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\#include <assert.h>
         \\typedef void Func();
@@ -937,7 +1014,7 @@ fn testFuncAddress(b: *Build, opts: Options) *Step {
         \\}
     , &.{});
     exe.linkLibrary(dso);
-    exe.force_pic = false;
+    exe.root_module.pic = false;
     exe.pie = false;
 
     const run = addRunArtifact(exe);
@@ -950,8 +1027,9 @@ fn testFuncAddress(b: *Build, opts: Options) *Step {
 fn testGcSections(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "gc-sections", opts);
 
-    const obj = addObject(b, "obj", opts);
-    addCppSourceBytes(obj,
+    const obj = addObject(b, opts, .{
+        .name = "obj",
+        .cpp_source_bytes =
         \\#include <stdio.h>
         \\int two() { return 2; }
         \\int live_var1 = 1;
@@ -966,14 +1044,15 @@ fn testGcSections(b: *Build, opts: Options) *Step {
         \\  printf("%d %d\n", live_var1, live_var2);
         \\  live_fn2();
         \\}
-    , &.{});
+        ,
+    });
     obj.link_function_sections = true;
     obj.link_data_sections = true;
     obj.linkLibC();
     obj.linkLibCpp();
 
     {
-        const exe = addExecutable(b, "test", opts);
+        const exe = addExecutable(b, opts, .{ .name = "test" });
         exe.addObject(obj);
         exe.link_gc_sections = false;
         exe.linkLibC();
@@ -1004,7 +1083,7 @@ fn testGcSections(b: *Build, opts: Options) *Step {
     }
 
     {
-        const exe = addExecutable(b, "test", opts);
+        const exe = addExecutable(b, opts, .{ .name = "test" });
         exe.addObject(obj);
         exe.link_gc_sections = true;
         exe.linkLibC();
@@ -1040,11 +1119,12 @@ fn testGcSections(b: *Build, opts: Options) *Step {
 fn testGcSectionsZig(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "gc-sections-zig", opts);
 
-    const obj = addObject(b, "obj", .{
+    const obj = addObject(b, .{
         .target = opts.target,
         .use_llvm = true,
-    });
-    addCSourceBytes(obj,
+    }, .{
+        .name = "obj",
+        .c_source_bytes =
         \\int live_var1 = 1;
         \\int live_var2 = 2;
         \\int dead_var1 = 3;
@@ -1053,13 +1133,15 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step {
         \\void live_fn2() { live_fn1(); }
         \\void dead_fn1() {}
         \\void dead_fn2() { dead_fn1(); }
-    , &.{});
+        ,
+    });
     obj.link_function_sections = true;
     obj.link_data_sections = true;
 
     {
-        const exe = addExecutable(b, "test1", opts);
-        addZigSourceBytes(exe,
+        const exe = addExecutable(b, opts, .{
+            .name = "test1",
+            .zig_source_bytes =
             \\const std = @import("std");
             \\extern var live_var1: i32;
             \\extern var live_var2: i32;
@@ -1069,7 +1151,8 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step {
             \\    stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable;
             \\    live_fn2();
             \\}
-        );
+            ,
+        });
         exe.addObject(obj);
         exe.link_gc_sections = false;
 
@@ -1098,8 +1181,9 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step {
     }
 
     {
-        const exe = addExecutable(b, "test2", opts);
-        addZigSourceBytes(exe,
+        const exe = addExecutable(b, opts, .{
+            .name = "test2",
+            .zig_source_bytes =
             \\const std = @import("std");
             \\extern var live_var1: i32;
             \\extern var live_var2: i32;
@@ -1109,7 +1193,8 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step {
             \\    stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable;
             \\    live_fn2();
             \\}
-        );
+            ,
+        });
         exe.addObject(obj);
         exe.link_gc_sections = true;
 
@@ -1143,7 +1228,7 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step {
 fn testHiddenWeakUndef(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "hidden-weak-undef", opts);
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(dso,
         \\__attribute__((weak, visibility("hidden"))) void foo();
         \\void bar() { foo(); }
@@ -1162,7 +1247,7 @@ fn testHiddenWeakUndef(b: *Build, opts: Options) *Step {
 fn testIFuncAlias(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "ifunc-alias", opts);
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\#include <assert.h>
         \\void foo() {}
@@ -1173,7 +1258,7 @@ fn testIFuncAlias(b: *Build, opts: Options) *Step {
         \\  assert(bar == bar2);
         \\}
     , &.{});
-    exe.force_pic = true;
+    exe.root_module.pic = true;
     exe.linkLibC();
     // https://github.com/ziglang/zig/issues/17619
     exe.pie = true;
@@ -1188,7 +1273,7 @@ fn testIFuncAlias(b: *Build, opts: Options) *Step {
 fn testIFuncDlopen(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "ifunc-dlopen", opts);
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(dso,
         \\__attribute__((ifunc("resolve_foo")))
         \\void foo(void);
@@ -1200,7 +1285,7 @@ fn testIFuncDlopen(b: *Build, opts: Options) *Step {
         \\}
     , &.{});
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\#include <dlfcn.h>
         \\#include <assert.h>
@@ -1219,7 +1304,7 @@ fn testIFuncDlopen(b: *Build, opts: Options) *Step {
     exe.linkLibrary(dso);
     exe.linkLibC();
     exe.linkSystemLibrary2("dl", .{});
-    exe.force_pic = false;
+    exe.root_module.pic = false;
     exe.pie = false;
 
     const run = addRunArtifact(exe);
@@ -1232,7 +1317,7 @@ fn testIFuncDlopen(b: *Build, opts: Options) *Step {
 fn testIFuncDso(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "ifunc-dso", opts);
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(dso,
         \\#include<stdio.h>
         \\__attribute__((ifunc("resolve_foobar")))
@@ -1247,7 +1332,7 @@ fn testIFuncDso(b: *Build, opts: Options) *Step {
     , &.{});
     dso.linkLibC();
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\void foobar(void);
         \\int main() {
@@ -1283,7 +1368,7 @@ fn testIFuncDynamic(b: *Build, opts: Options) *Step {
     ;
 
     {
-        const exe = addExecutable(b, "main", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main" });
         addCSourceBytes(exe, main_c, &.{});
         exe.linkLibC();
         exe.link_z_lazy = true;
@@ -1295,7 +1380,7 @@ fn testIFuncDynamic(b: *Build, opts: Options) *Step {
         test_step.dependOn(&run.step);
     }
     {
-        const exe = addExecutable(b, "other", opts);
+        const exe = addExecutable(b, opts, .{ .name = "other" });
         addCSourceBytes(exe, main_c, &.{});
         exe.linkLibC();
         // https://github.com/ziglang/zig/issues/17619
@@ -1312,7 +1397,7 @@ fn testIFuncDynamic(b: *Build, opts: Options) *Step {
 fn testIFuncExport(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "ifunc-export", opts);
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(dso,
         \\#include <stdio.h>
         \\__attribute__((ifunc("resolve_foobar")))
@@ -1338,7 +1423,7 @@ fn testIFuncExport(b: *Build, opts: Options) *Step {
 fn testIFuncFuncPtr(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "ifunc-func-ptr", opts);
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\typedef int Fn();
         \\int foo() __attribute__((ifunc("resolve_foo")));
@@ -1361,7 +1446,7 @@ fn testIFuncFuncPtr(b: *Build, opts: Options) *Step {
         \\  printf("%d\n", f());
         \\}
     , &.{});
-    exe.force_pic = true;
+    exe.root_module.pic = true;
     exe.linkLibC();
     // https://github.com/ziglang/zig/issues/17619
     exe.pie = true;
@@ -1376,7 +1461,7 @@ fn testIFuncFuncPtr(b: *Build, opts: Options) *Step {
 fn testIFuncNoPlt(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "ifunc-noplt", opts);
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\#include <stdio.h>
         \\__attribute__((ifunc("resolve_foo")))
@@ -1392,7 +1477,7 @@ fn testIFuncNoPlt(b: *Build, opts: Options) *Step {
         \\  foo();
         \\}
     , &.{"-fno-plt"});
-    exe.force_pic = true;
+    exe.root_module.pic = true;
     exe.linkLibC();
     // https://github.com/ziglang/zig/issues/17619
     exe.pie = true;
@@ -1407,7 +1492,7 @@ fn testIFuncNoPlt(b: *Build, opts: Options) *Step {
 fn testIFuncStatic(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "ifunc-static", opts);
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\#include <stdio.h>
         \\void foo() __attribute__((ifunc("resolve_foo")));
@@ -1435,7 +1520,7 @@ fn testIFuncStatic(b: *Build, opts: Options) *Step {
 fn testIFuncStaticPie(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "ifunc-static-pie", opts);
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\#include <stdio.h>
         \\void foo() __attribute__((ifunc("resolve_foo")));
@@ -1451,7 +1536,7 @@ fn testIFuncStaticPie(b: *Build, opts: Options) *Step {
         \\}
     , &.{});
     exe.linkage = .static;
-    exe.force_pic = true;
+    exe.root_module.pic = true;
     exe.pie = true;
     exe.linkLibC();
 
@@ -1478,7 +1563,7 @@ fn testImageBase(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "image-base", opts);
 
     {
-        const exe = addExecutable(b, "main1", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main1" });
         addCSourceBytes(exe,
             \\#include <stdio.h>
             \\int main() {
@@ -1502,7 +1587,7 @@ fn testImageBase(b: *Build, opts: Options) *Step {
     }
 
     {
-        const exe = addExecutable(b, "main2", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main2" });
         addCSourceBytes(exe, "void _start() {}", &.{});
         exe.image_base = 0xffffffff8000000;
 
@@ -1520,22 +1605,26 @@ fn testImageBase(b: *Build, opts: Options) *Step {
 fn testImportingDataDynamic(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "importing-data-dynamic", opts);
 
-    const dso = addSharedLibrary(b, "a", .{
+    const dso = addSharedLibrary(b, .{
         .target = opts.target,
         .optimize = opts.optimize,
         .use_llvm = true,
+    }, .{
+        .name = "a",
+        .c_source_bytes = "int foo = 42;",
     });
-    addCSourceBytes(dso, "int foo = 42;", &.{});
 
-    const main = addExecutable(b, "main", opts);
-    addZigSourceBytes(main,
+    const main = addExecutable(b, opts, .{
+        .name = "main",
+        .zig_source_bytes =
         \\extern var foo: i32;
         \\pub fn main() void {
         \\    @import("std").debug.print("{d}\n", .{foo});
         \\}
-    );
+        ,
+        .strip = true, // TODO temp hack
+    });
     main.pie = true;
-    main.strip = true; // TODO temp hack
     main.linkLibrary(dso);
     main.linkLibC();
 
@@ -1549,28 +1638,34 @@ fn testImportingDataDynamic(b: *Build, opts: Options) *Step {
 fn testImportingDataStatic(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "importing-data-static", opts);
 
-    const obj = addObject(b, "a", .{
+    const obj = addObject(b, .{
         .target = opts.target,
         .optimize = opts.optimize,
         .use_llvm = true,
+    }, .{
+        .name = "a",
+        .c_source_bytes = "int foo = 42;",
     });
-    addCSourceBytes(obj, "int foo = 42;", &.{});
 
-    const lib = addStaticLibrary(b, "a", .{
+    const lib = addStaticLibrary(b, .{
         .target = opts.target,
         .optimize = opts.optimize,
         .use_llvm = true,
+    }, .{
+        .name = "a",
     });
     lib.addObject(obj);
 
-    const main = addExecutable(b, "main", opts);
-    addZigSourceBytes(main,
+    const main = addExecutable(b, opts, .{
+        .name = "main",
+        .zig_source_bytes =
         \\extern var foo: i32;
         \\pub fn main() void {
         \\    @import("std").debug.print("{d}\n", .{foo});
         \\}
-    );
-    main.strip = true; // TODO temp hack
+        ,
+        .strip = true, // TODO temp hack
+    });
     main.linkLibrary(lib);
     main.linkLibC();
 
@@ -1584,63 +1679,76 @@ fn testImportingDataStatic(b: *Build, opts: Options) *Step {
 fn testInitArrayOrder(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "init-array-order", opts);
 
-    const a_o = addObject(b, "a", opts);
-    addCSourceBytes(a_o,
+    const a_o = addObject(b, opts, .{
+        .name = "a",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\__attribute__((constructor(10000))) void init4() { printf("1"); }
-    , &.{});
+        ,
+    });
     a_o.linkLibC();
 
-    const b_o = addObject(b, "b", opts);
-    addCSourceBytes(b_o,
+    const b_o = addObject(b, opts, .{
+        .name = "b",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\__attribute__((constructor(1000))) void init3() { printf("2"); }
-    , &.{});
+        ,
+    });
     b_o.linkLibC();
 
-    const c_o = addObject(b, "c", opts);
-    addCSourceBytes(c_o,
+    const c_o = addObject(b, opts, .{
+        .name = "c",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\__attribute__((constructor)) void init1() { printf("3"); }
-    , &.{});
+        ,
+    });
     c_o.linkLibC();
 
-    const d_o = addObject(b, "d", opts);
-    addCSourceBytes(d_o,
+    const d_o = addObject(b, opts, .{
+        .name = "d",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\__attribute__((constructor)) void init2() { printf("4"); }
-    , &.{});
+        ,
+    });
     d_o.linkLibC();
 
-    const e_o = addObject(b, "e", opts);
-    addCSourceBytes(e_o,
+    const e_o = addObject(b, opts, .{
+        .name = "e",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\__attribute__((destructor(10000))) void fini4() { printf("5"); }
-    , &.{});
+        ,
+    });
     e_o.linkLibC();
 
-    const f_o = addObject(b, "f", opts);
-    addCSourceBytes(f_o,
+    const f_o = addObject(b, opts, .{
+        .name = "f",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\__attribute__((destructor(1000))) void fini3() { printf("6"); }
-    , &.{});
+        ,
+    });
     f_o.linkLibC();
 
-    const g_o = addObject(b, "g", opts);
-    addCSourceBytes(g_o,
+    const g_o = addObject(b, opts, .{
+        .name = "g",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\__attribute__((destructor)) void fini1() { printf("7"); }
-    , &.{});
+        ,
+    });
     g_o.linkLibC();
 
-    const h_o = addObject(b, "h", opts);
-    addCSourceBytes(h_o,
-        \\#include <stdio.h>
-        \\__attribute__((destructor)) void fini2() { printf("8"); }
-    , &.{});
+    const h_o = addObject(b, opts, .{ .name = "h", .c_source_bytes = 
+    \\#include <stdio.h>
+    \\__attribute__((destructor)) void fini2() { printf("8"); }
+    });
     h_o.linkLibC();
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe, "int main() { return 0; }", &.{});
     exe.addObject(a_o);
     exe.addObject(b_o);
@@ -1651,7 +1759,7 @@ fn testInitArrayOrder(b: *Build, opts: Options) *Step {
     exe.addObject(g_o);
     exe.addObject(h_o);
 
-    if (opts.target.isGnuLibC()) {
+    if (opts.target.target.isGnuLibC()) {
         // TODO I think we need to clarify our use of `-fPIC -fPIE` flags for different targets
         exe.pie = true;
     }
@@ -1666,7 +1774,7 @@ fn testInitArrayOrder(b: *Build, opts: Options) *Step {
 fn testLargeAlignmentDso(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "large-alignment-dso", opts);
 
-    const dso = addSharedLibrary(b, "dso", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "dso" });
     addCSourceBytes(dso,
         \\#include <stdio.h>
         \\#include <stdint.h>
@@ -1695,7 +1803,7 @@ fn testLargeAlignmentDso(b: *Build, opts: Options) *Step {
     check.checkComputeCompare("addr2 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
     test_step.dependOn(&check.step);
 
-    const exe = addExecutable(b, "test", opts);
+    const exe = addExecutable(b, opts, .{ .name = "test" });
     addCSourceBytes(exe,
         \\void greet();
         \\int main() { greet(); }
@@ -1715,7 +1823,7 @@ fn testLargeAlignmentDso(b: *Build, opts: Options) *Step {
 fn testLargeAlignmentExe(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "large-alignment-exe", opts);
 
-    const exe = addExecutable(b, "test", opts);
+    const exe = addExecutable(b, opts, .{ .name = "test" });
     addCSourceBytes(exe,
         \\#include <stdio.h>
         \\#include <stdint.h>
@@ -1760,7 +1868,7 @@ fn testLargeAlignmentExe(b: *Build, opts: Options) *Step {
 fn testLargeBss(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "large-bss", opts);
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\char arr[0x100000000];
         \\int main() {
@@ -1781,27 +1889,31 @@ fn testLargeBss(b: *Build, opts: Options) *Step {
 fn testLinkOrder(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "link-order", opts);
 
-    const obj = addObject(b, "obj", opts);
-    addCSourceBytes(obj, "void foo() {}", &.{});
-    obj.force_pic = true;
+    const obj = addObject(b, opts, .{
+        .name = "obj",
+        .c_source_bytes = "void foo() {}",
+        .pic = true,
+    });
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     dso.addObject(obj);
 
-    const lib = addStaticLibrary(b, "b", opts);
+    const lib = addStaticLibrary(b, opts, .{ .name = "b" });
     lib.addObject(obj);
 
-    const main_o = addObject(b, "main", opts);
-    addCSourceBytes(main_o,
+    const main_o = addObject(b, opts, .{
+        .name = "main",
+        .c_source_bytes =
         \\void foo();
         \\int main() {
         \\  foo();
         \\}
-    , &.{});
+        ,
+    });
 
     // https://github.com/ziglang/zig/issues/17450
     // {
-    //     const exe = addExecutable(b, "main1", opts);
+    //     const exe = addExecutable(b, opts, .{ .name = "main1"});
     //     exe.addObject(main_o);
     //     exe.linkSystemLibrary2("a", .{});
     //     exe.addLibraryPath(dso.getEmittedBinDirectory());
@@ -1818,7 +1930,7 @@ fn testLinkOrder(b: *Build, opts: Options) *Step {
     // }
 
     {
-        const exe = addExecutable(b, "main2", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main2" });
         exe.addObject(main_o);
         exe.linkSystemLibrary2("b", .{});
         exe.addLibraryPath(lib.getEmittedBinDirectory());
@@ -1840,14 +1952,14 @@ fn testLinkOrder(b: *Build, opts: Options) *Step {
 fn testLdScript(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "ld-script", opts);
 
-    const dso = addSharedLibrary(b, "bar", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "bar" });
     addCSourceBytes(dso, "int foo() { return 42; }", &.{});
 
     const scripts = WriteFile.create(b);
     _ = scripts.add("liba.so", "INPUT(libfoo.so)");
     _ = scripts.add("libfoo.so", "GROUP(AS_NEEDED(-lbar))");
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\int foo();
         \\int main() {
@@ -1875,7 +1987,7 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {
     const scripts = WriteFile.create(b);
     _ = scripts.add("liba.so", "INPUT(libfoo.so)");
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe, "int main() { return 0; }", &.{});
     exe.linkSystemLibrary2("a", .{});
     exe.addLibraryPath(scripts.getDirectory());
@@ -1895,13 +2007,15 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {
 fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "mismatched-cpu-architecture-error", opts);
 
-    const obj = addObject(b, "a", .{
-        .target = .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .gnu },
+    const obj = addObject(b, .{
+        .target = b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .gnu }),
+    }, .{
+        .name = "a",
+        .c_source_bytes = "int foo;",
+        .strip = true,
     });
-    addCSourceBytes(obj, "int foo;", &.{});
-    obj.strip = true;
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\extern int foo;
         \\int main() {
@@ -1922,7 +2036,7 @@ fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step {
 fn testLinkingC(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "linking-c", opts);
 
-    const exe = addExecutable(b, "test", opts);
+    const exe = addExecutable(b, opts, .{ .name = "test" });
     addCSourceBytes(exe,
         \\#include <stdio.h>
         \\int main() {
@@ -1951,7 +2065,7 @@ fn testLinkingC(b: *Build, opts: Options) *Step {
 fn testLinkingCpp(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "linking-cpp", opts);
 
-    const exe = addExecutable(b, "test", opts);
+    const exe = addExecutable(b, opts, .{ .name = "test" });
     addCppSourceBytes(exe,
         \\#include <iostream>
         \\int main() {
@@ -1981,24 +2095,28 @@ fn testLinkingCpp(b: *Build, opts: Options) *Step {
 fn testLinkingObj(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "linking-obj", opts);
 
-    const obj = addObject(b, "aobj", opts);
-    addZigSourceBytes(obj,
+    const obj = addObject(b, opts, .{
+        .name = "aobj",
+        .zig_source_bytes =
         \\extern var mod: usize;
         \\export fn callMe() usize {
         \\    return me * mod;
         \\}
         \\var me: usize = 42;
-    );
+        ,
+    });
 
-    const exe = addExecutable(b, "testobj", opts);
-    addZigSourceBytes(exe,
+    const exe = addExecutable(b, opts, .{
+        .name = "testobj",
+        .zig_source_bytes =
         \\const std = @import("std");
         \\extern fn callMe() usize;
         \\export var mod: usize = 2;
         \\pub fn main() void {
         \\    std.debug.print("{d}\n", .{callMe()});
         \\}
-    );
+        ,
+    });
     exe.addObject(obj);
 
     const run = addRunArtifact(exe);
@@ -2011,26 +2129,32 @@ fn testLinkingObj(b: *Build, opts: Options) *Step {
 fn testLinkingStaticLib(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "linking-static-lib", opts);
 
-    const obj = addObject(b, "bobj", opts);
-    addZigSourceBytes(obj, "export var bar: i32 = -42;");
+    const obj = addObject(b, opts, .{
+        .name = "bobj",
+        .zig_source_bytes = "export var bar: i32 = -42;",
+    });
 
-    const lib = addStaticLibrary(b, "alib", opts);
-    addZigSourceBytes(lib,
+    const lib = addStaticLibrary(b, opts, .{
+        .name = "alib",
+        .zig_source_bytes =
         \\export fn foo() i32 {
         \\    return 42;
         \\}
-    );
+        ,
+    });
     lib.addObject(obj);
 
-    const exe = addExecutable(b, "testlib", opts);
-    addZigSourceBytes(exe,
+    const exe = addExecutable(b, opts, .{
+        .name = "testlib",
+        .zig_source_bytes =
         \\const std = @import("std");
         \\extern fn foo() i32;
         \\extern var bar: i32;
         \\pub fn main() void {
         \\    std.debug.print("{d}\n", .{foo() + bar});
         \\}
-    );
+        ,
+    });
     exe.linkLibrary(lib);
 
     const run = addRunArtifact(exe);
@@ -2043,12 +2167,14 @@ fn testLinkingStaticLib(b: *Build, opts: Options) *Step {
 fn testLinkingZig(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "linking-zig-static", opts);
 
-    const exe = addExecutable(b, "test", opts);
-    addZigSourceBytes(exe,
+    const exe = addExecutable(b, opts, .{
+        .name = "test",
+        .zig_source_bytes =
         \\pub fn main() void {
         \\    @import("std").debug.print("Hello World!\n", .{});
         \\}
-    );
+        ,
+    });
 
     const run = addRunArtifact(exe);
     run.expectStdErrEqual("Hello World!\n");
@@ -2069,7 +2195,7 @@ fn testLinkingZig(b: *Build, opts: Options) *Step {
 fn testNoEhFrameHdr(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "no-eh-frame-hdr", opts);
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe, "int main() { return 0; }", &.{});
     exe.link_eh_frame_hdr = false;
     exe.linkLibC();
@@ -2086,7 +2212,7 @@ fn testNoEhFrameHdr(b: *Build, opts: Options) *Step {
 fn testPie(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "hello-pie", opts);
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\#include <stdio.h>
         \\int main() {
@@ -2095,7 +2221,7 @@ fn testPie(b: *Build, opts: Options) *Step {
         \\}
     , &.{});
     exe.linkLibC();
-    exe.force_pic = true;
+    exe.root_module.pic = true;
     exe.pie = true;
 
     const run = addRunArtifact(exe);
@@ -2117,7 +2243,7 @@ fn testPie(b: *Build, opts: Options) *Step {
 fn testPltGot(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "plt-got", opts);
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(dso,
         \\#include <stdio.h>
         \\void ignore(void *foo) {}
@@ -2127,7 +2253,7 @@ fn testPltGot(b: *Build, opts: Options) *Step {
     , &.{});
     dso.linkLibC();
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\void ignore(void *);
         \\int hello();
@@ -2135,7 +2261,7 @@ fn testPltGot(b: *Build, opts: Options) *Step {
         \\int main() { hello(); }
     , &.{});
     exe.linkLibrary(dso);
-    exe.force_pic = true;
+    exe.root_module.pic = true;
     exe.linkLibC();
     // https://github.com/ziglang/zig/issues/17619
     exe.pie = true;
@@ -2151,10 +2277,12 @@ fn testPreinitArray(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "preinit-array", opts);
 
     {
-        const obj = addObject(b, "obj", opts);
-        addCSourceBytes(obj, "void _start() {}", &.{});
+        const obj = addObject(b, opts, .{
+            .name = "obj",
+            .c_source_bytes = "void _start() {}",
+        });
 
-        const exe = addExecutable(b, "main1", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main1" });
         exe.addObject(obj);
 
         const check = exe.checkObject();
@@ -2163,7 +2291,7 @@ fn testPreinitArray(b: *Build, opts: Options) *Step {
     }
 
     {
-        const exe = addExecutable(b, "main2", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main2" });
         addCSourceBytes(exe,
             \\void preinit_fn() {}
             \\int main() {}
@@ -2183,38 +2311,48 @@ fn testPreinitArray(b: *Build, opts: Options) *Step {
 fn testRelocatableArchive(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "relocatable-archive", opts);
 
-    const obj1 = addObject(b, "obj1", opts);
-    addCSourceBytes(obj1,
+    const obj1 = addObject(b, opts, .{
+        .name = "obj1",
+        .c_source_bytes =
         \\void bar();
         \\void foo() {
         \\  bar();
         \\}
-    , &.{});
+        ,
+    });
 
-    const obj2 = addObject(b, "obj2", opts);
-    addCSourceBytes(obj2,
+    const obj2 = addObject(b, opts, .{
+        .name = "obj2",
+        .c_source_bytes =
         \\void bar() {}
-    , &.{});
+        ,
+    });
 
-    const obj3 = addObject(b, "obj3", opts);
-    addCSourceBytes(obj3,
+    const obj3 = addObject(b, opts, .{
+        .name = "obj3",
+        .c_source_bytes =
         \\void baz();
-    , &.{});
+        ,
+    });
 
-    const obj4 = addObject(b, "obj4", opts);
-    addCSourceBytes(obj4,
+    const obj4 = addObject(b, opts, .{
+        .name = "obj4",
+        .c_source_bytes =
         \\void foo();
         \\int main() {
         \\  foo();
         \\}
-    , &.{});
+        ,
+    });
 
-    const lib = addStaticLibrary(b, "lib", opts);
+    const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
     lib.addObject(obj1);
     lib.addObject(obj2);
     lib.addObject(obj3);
 
-    const obj5 = addObject(b, "obj5", opts);
+    const obj5 = addObject(b, opts, .{
+        .name = "obj5",
+    });
     obj5.addObject(obj4);
     obj5.linkLibrary(lib);
 
@@ -2234,13 +2372,15 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "relocatable-eh-frame", opts);
 
     {
-        const obj = addObject(b, "obj1", opts);
-        addCppSourceBytes(obj,
+        const obj = addObject(b, opts, .{
+            .name = "obj1",
+            .cpp_source_bytes =
             \\#include <stdexcept>
             \\int try_me() {
             \\  throw std::runtime_error("Oh no!");
             \\}
-        , &.{});
+            ,
+        });
         addCppSourceBytes(obj,
             \\extern int try_me();
             \\int try_again() {
@@ -2249,7 +2389,7 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {
         , &.{});
         obj.linkLibCpp();
 
-        const exe = addExecutable(b, "test1", opts);
+        const exe = addExecutable(b, opts, .{ .name = "test1" });
         addCppSourceBytes(exe,
             \\#include <iostream>
             \\#include <stdexcept>
@@ -2273,13 +2413,15 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {
 
     {
         // Let's make the object file COMDAT group heavy!
-        const obj = addObject(b, "obj2", opts);
-        addCppSourceBytes(obj,
+        const obj = addObject(b, opts, .{
+            .name = "obj2",
+            .cpp_source_bytes =
             \\#include <stdexcept>
             \\int try_me() {
             \\  throw std::runtime_error("Oh no!");
             \\}
-        , &.{});
+            ,
+        });
         addCppSourceBytes(obj,
             \\extern int try_me();
             \\int try_again() {
@@ -2301,7 +2443,7 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {
         , &.{});
         obj.linkLibCpp();
 
-        const exe = addExecutable(b, "test2", opts);
+        const exe = addExecutable(b, opts, .{ .name = "test2" });
         exe.addObject(obj);
         exe.linkLibCpp();
 
@@ -2316,13 +2458,18 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {
 fn testRelocatableNoEhFrame(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "relocatable-no-eh-frame", opts);
 
-    const obj1 = addObject(b, "obj1", opts);
-    addCSourceBytes(obj1, "int bar() { return 42; }", &.{
-        "-fno-unwind-tables",
-        "-fno-asynchronous-unwind-tables",
+    const obj1 = addObject(b, opts, .{
+        .name = "obj1",
+        .c_source_bytes = "int bar() { return 42; }",
+        .c_source_flags = &.{
+            "-fno-unwind-tables",
+            "-fno-asynchronous-unwind-tables",
+        },
     });
 
-    const obj2 = addObject(b, "obj2", opts);
+    const obj2 = addObject(b, opts, .{
+        .name = "obj2",
+    });
     obj2.addObject(obj1);
 
     const check1 = obj1.checkObject();
@@ -2343,23 +2490,25 @@ fn testRelocatableNoEhFrame(b: *Build, opts: Options) *Step {
 fn testSharedAbsSymbol(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "shared-abs-symbol", opts);
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addAsmSourceBytes(dso,
         \\.globl foo
         \\foo = 3;
     );
 
-    const obj = addObject(b, "obj", opts);
-    addCSourceBytes(obj,
+    const obj = addObject(b, opts, .{
+        .name = "obj",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\extern char foo;
         \\int main() { printf("foo=%p\n", &foo); }
-    , &.{});
-    obj.force_pic = true;
+        ,
+        .pic = true,
+    });
     obj.linkLibC();
 
     {
-        const exe = addExecutable(b, "main1", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main1" });
         exe.addObject(obj);
         exe.linkLibrary(dso);
         exe.pie = true;
@@ -2380,7 +2529,7 @@ fn testSharedAbsSymbol(b: *Build, opts: Options) *Step {
 
     // https://github.com/ziglang/zig/issues/17430
     // {
-    //     const exe = addExecutable(b, "main2", opts);
+    //     const exe = addExecutable(b, opts, .{ .name = "main2"});
     //     exe.addObject(obj);
     //     exe.linkLibrary(dso);
     //     exe.pie = false;
@@ -2405,20 +2554,22 @@ fn testSharedAbsSymbol(b: *Build, opts: Options) *Step {
 fn testStrip(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "strip", opts);
 
-    const obj = addObject(b, "obj", opts);
-    addCSourceBytes(obj,
+    const obj = addObject(b, opts, .{
+        .name = "obj",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\int main() {
         \\  printf("Hello!\n");
         \\  return 0;
         \\}
-    , &.{});
+        ,
+    });
     obj.linkLibC();
 
     {
-        const exe = addExecutable(b, "main1", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main1" });
         exe.addObject(obj);
-        exe.strip = false;
+        exe.root_module.strip = false;
         exe.linkLibC();
 
         const check = exe.checkObject();
@@ -2429,9 +2580,9 @@ fn testStrip(b: *Build, opts: Options) *Step {
     }
 
     {
-        const exe = addExecutable(b, "main2", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main2" });
         exe.addObject(obj);
-        exe.strip = true;
+        exe.root_module.strip = true;
         exe.linkLibC();
 
         const check = exe.checkObject();
@@ -2447,16 +2598,19 @@ fn testStrip(b: *Build, opts: Options) *Step {
 fn testTlsDfStaticTls(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-df-static-tls", opts);
 
-    const obj = addObject(b, "obj", opts);
-    addCSourceBytes(obj,
+    const obj = addObject(b, opts, .{
+        .name = "obj",
+        .c_source_bytes =
         \\static _Thread_local int foo = 5;
         \\void mutate() { ++foo; }
         \\int bar() { return foo; }
-    , &.{"-ftls-model=initial-exec"});
-    obj.force_pic = true;
+        ,
+        .c_source_flags = &.{"-ftls-model=initial-exec"},
+        .pic = true,
+    });
 
     {
-        const dso = addSharedLibrary(b, "a", opts);
+        const dso = addSharedLibrary(b, opts, .{ .name = "a" });
         dso.addObject(obj);
         // dso.link_relax = true;
 
@@ -2468,7 +2622,7 @@ fn testTlsDfStaticTls(b: *Build, opts: Options) *Step {
 
     // TODO add -Wl,--no-relax
     // {
-    //     const dso = addSharedLibrary(b, "a", opts);
+    //     const dso = addSharedLibrary(b, opts, .{ .name = "a"});
     //     dso.addObject(obj);
     //     dso.link_relax = false;
 
@@ -2484,7 +2638,7 @@ fn testTlsDfStaticTls(b: *Build, opts: Options) *Step {
 fn testTlsDso(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-dso", opts);
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(dso,
         \\extern _Thread_local int foo;
         \\_Thread_local int bar;
@@ -2492,7 +2646,7 @@ fn testTlsDso(b: *Build, opts: Options) *Step {
         \\int get_bar1() { return bar; }
     , &.{});
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\#include <stdio.h>
         \\_Thread_local int foo;
@@ -2526,8 +2680,9 @@ fn testTlsDso(b: *Build, opts: Options) *Step {
 fn testTlsGd(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-gd", opts);
 
-    const main_o = addObject(b, "main", opts);
-    addCSourceBytes(main_o,
+    const main_o = addObject(b, opts, .{
+        .name = "main",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
         \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x2;
@@ -2540,37 +2695,42 @@ fn testTlsGd(b: *Build, opts: Options) *Step {
         \\  printf("%d %d %d %d %d %d\n", x1, x2, x3, x4, get_x5(), get_x6());
         \\  return 0;
         \\}
-    , &.{});
+        ,
+        .pic = true,
+    });
     main_o.linkLibC();
-    main_o.force_pic = true;
 
-    const a_o = addObject(b, "a", opts);
-    addCSourceBytes(a_o,
+    const a_o = addObject(b, opts, .{
+        .name = "a",
+        .c_source_bytes =
         \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3;
         \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5;
         \\int get_x5() { return x5; }
-    , &.{});
-    a_o.force_pic = true;
+        ,
+        .pic = true,
+    });
 
-    const b_o = addObject(b, "b", opts);
-    addCSourceBytes(b_o,
+    const b_o = addObject(b, opts, .{
+        .name = "b",
+        .c_source_bytes =
         \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4;
         \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6;
         \\int get_x6() { return x6; }
-    , &.{});
-    b_o.force_pic = true;
+        ,
+        .pic = true,
+    });
 
     const exp_stdout = "1 2 3 4 5 6\n";
 
-    const dso1 = addSharedLibrary(b, "a", opts);
+    const dso1 = addSharedLibrary(b, opts, .{ .name = "a" });
     dso1.addObject(a_o);
 
-    const dso2 = addSharedLibrary(b, "b", opts);
+    const dso2 = addSharedLibrary(b, opts, .{ .name = "b" });
     dso2.addObject(b_o);
     // dso2.link_relax = false; // TODO
 
     {
-        const exe = addExecutable(b, "main1", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main1" });
         exe.addObject(main_o);
         exe.linkLibrary(dso1);
         exe.linkLibrary(dso2);
@@ -2581,7 +2741,7 @@ fn testTlsGd(b: *Build, opts: Options) *Step {
     }
 
     {
-        const exe = addExecutable(b, "main2", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main2" });
         exe.addObject(main_o);
         // exe.link_relax = false; // TODO
         exe.linkLibrary(dso1);
@@ -2594,7 +2754,7 @@ fn testTlsGd(b: *Build, opts: Options) *Step {
 
     // https://github.com/ziglang/zig/issues/17430 ??
     // {
-    //     const exe = addExecutable(b, "main3", opts);
+    //     const exe = addExecutable(b, opts, .{ .name = "main3"});
     //     exe.addObject(main_o);
     //     exe.linkLibrary(dso1);
     //     exe.linkLibrary(dso2);
@@ -2606,7 +2766,7 @@ fn testTlsGd(b: *Build, opts: Options) *Step {
     // }
 
     // {
-    //     const exe = addExecutable(b, "main4", opts);
+    //     const exe = addExecutable(b, opts, .{ .name = "main4"});
     //     exe.addObject(main_o);
     //     // exe.link_relax = false; // TODO
     //     exe.linkLibrary(dso1);
@@ -2624,8 +2784,9 @@ fn testTlsGd(b: *Build, opts: Options) *Step {
 fn testTlsGdNoPlt(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-gd-no-plt", opts);
 
-    const obj = addObject(b, "obj", opts);
-    addCSourceBytes(obj,
+    const obj = addObject(b, opts, .{
+        .name = "obj",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
         \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x2;
@@ -2639,18 +2800,20 @@ fn testTlsGdNoPlt(b: *Build, opts: Options) *Step {
         \\  printf("%d %d %d %d %d %d\n", x1, x2, x3, x4, get_x5(), get_x6());
         \\  return 0;
         \\}
-    , &.{"-fno-plt"});
-    obj.force_pic = true;
+        ,
+        .c_source_flags = &.{"-fno-plt"},
+        .pic = true,
+    });
     obj.linkLibC();
 
-    const a_so = addSharedLibrary(b, "a", opts);
+    const a_so = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(a_so,
         \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3;
         \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5;
         \\int get_x5() { return x5; }
     , &.{"-fno-plt"});
 
-    const b_so = addSharedLibrary(b, "b", opts);
+    const b_so = addSharedLibrary(b, opts, .{ .name = "b" });
     addCSourceBytes(b_so,
         \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4;
         \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6;
@@ -2659,7 +2822,7 @@ fn testTlsGdNoPlt(b: *Build, opts: Options) *Step {
     // b_so.link_relax = false; // TODO
 
     {
-        const exe = addExecutable(b, "main1", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main1" });
         exe.addObject(obj);
         exe.linkLibrary(a_so);
         exe.linkLibrary(b_so);
@@ -2673,7 +2836,7 @@ fn testTlsGdNoPlt(b: *Build, opts: Options) *Step {
     }
 
     {
-        const exe = addExecutable(b, "main2", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main2" });
         exe.addObject(obj);
         exe.linkLibrary(a_so);
         exe.linkLibrary(b_so);
@@ -2693,8 +2856,9 @@ fn testTlsGdNoPlt(b: *Build, opts: Options) *Step {
 fn testTlsGdToIe(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-gd-to-ie", opts);
 
-    const a_o = addObject(b, "a", opts);
-    addCSourceBytes(a_o,
+    const a_o = addObject(b, opts, .{
+        .name = "a",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
         \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x2 = 2;
@@ -2705,22 +2869,25 @@ fn testTlsGdToIe(b: *Build, opts: Options) *Step {
         \\  printf("%d %d %d\n", x1, x2, x3);
         \\  return 0;
         \\}
-    , &.{});
+        ,
+        .pic = true,
+    });
     a_o.linkLibC();
-    a_o.force_pic = true;
 
-    const b_o = addObject(b, "b", opts);
-    addCSourceBytes(b_o,
+    const b_o = addObject(b, opts, .{
+        .name = "b",
+        .c_source_bytes =
         \\int foo();
         \\int main() { foo(); }
-    , &.{});
-    b_o.force_pic = true;
+        ,
+        .pic = true,
+    });
 
     {
-        const dso = addSharedLibrary(b, "a1", opts);
+        const dso = addSharedLibrary(b, opts, .{ .name = "a1" });
         dso.addObject(a_o);
 
-        const exe = addExecutable(b, "main1", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main1" });
         exe.addObject(b_o);
         exe.linkLibrary(dso);
         exe.linkLibC();
@@ -2733,11 +2900,11 @@ fn testTlsGdToIe(b: *Build, opts: Options) *Step {
     }
 
     {
-        const dso = addSharedLibrary(b, "a2", opts);
+        const dso = addSharedLibrary(b, opts, .{ .name = "a2" });
         dso.addObject(a_o);
         // dso.link_relax = false; // TODO
 
-        const exe = addExecutable(b, "main2", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main2" });
         exe.addObject(b_o);
         exe.linkLibrary(dso);
         exe.linkLibC();
@@ -2750,11 +2917,11 @@ fn testTlsGdToIe(b: *Build, opts: Options) *Step {
     }
 
     // {
-    //     const dso = addSharedLibrary(b, "a", opts);
+    //     const dso = addSharedLibrary(b, opts, .{ .name = "a"});
     //     dso.addObject(a_o);
     //     dso.link_z_nodlopen = true;
 
-    //     const exe = addExecutable(b, "main", opts);
+    //     const exe = addExecutable(b, opts, .{ .name = "main"});
     //     exe.addObject(b_o);
     //     exe.linkLibrary(dso);
 
@@ -2764,12 +2931,12 @@ fn testTlsGdToIe(b: *Build, opts: Options) *Step {
     // }
 
     // {
-    //     const dso = addSharedLibrary(b, "a", opts);
+    //     const dso = addSharedLibrary(b, opts, .{ .name = "a"});
     //     dso.addObject(a_o);
     //     dso.link_relax = false;
     //     dso.link_z_nodlopen = true;
 
-    //     const exe = addExecutable(b, "main", opts);
+    //     const exe = addExecutable(b, opts, .{ .name = "main"});
     //     exe.addObject(b_o);
     //     exe.linkLibrary(dso);
 
@@ -2784,7 +2951,7 @@ fn testTlsGdToIe(b: *Build, opts: Options) *Step {
 fn testTlsIe(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-ie", opts);
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(dso,
         \\#include <stdio.h>
         \\__attribute__((tls_model("initial-exec"))) static _Thread_local int foo;
@@ -2799,8 +2966,9 @@ fn testTlsIe(b: *Build, opts: Options) *Step {
     , &.{});
     dso.linkLibC();
 
-    const main_o = addObject(b, "main", opts);
-    addCSourceBytes(main_o,
+    const main_o = addObject(b, opts, .{
+        .name = "main",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\_Thread_local int baz;
         \\void set();
@@ -2812,13 +2980,14 @@ fn testTlsIe(b: *Build, opts: Options) *Step {
         \\  print();
         \\  printf("%d\n", baz);
         \\}
-    , &.{});
+        ,
+    });
     main_o.linkLibC();
 
     const exp_stdout = "0 0 3 5 7\n";
 
     {
-        const exe = addExecutable(b, "main1", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main1" });
         exe.addObject(main_o);
         exe.linkLibrary(dso);
         exe.linkLibC();
@@ -2831,7 +3000,7 @@ fn testTlsIe(b: *Build, opts: Options) *Step {
     }
 
     {
-        const exe = addExecutable(b, "main2", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main2" });
         exe.addObject(main_o);
         exe.linkLibrary(dso);
         exe.linkLibC();
@@ -2850,38 +3019,46 @@ fn testTlsIe(b: *Build, opts: Options) *Step {
 fn testTlsLargeAlignment(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-large-alignment", opts);
 
-    const a_o = addObject(b, "a", opts);
-    addCSourceBytes(a_o,
+    const a_o = addObject(b, opts, .{
+        .name = "a",
+        .c_source_bytes =
         \\__attribute__((section(".tdata1")))
         \\_Thread_local int x = 42;
-    , &.{"-std=c11"});
-    a_o.force_pic = true;
+        ,
+        .c_source_flags = &.{"-std=c11"},
+        .pic = true,
+    });
 
-    const b_o = addObject(b, "b", opts);
-    addCSourceBytes(b_o,
+    const b_o = addObject(b, opts, .{
+        .name = "b",
+        .c_source_bytes =
         \\__attribute__((section(".tdata2")))
         \\_Alignas(256) _Thread_local int y[] = { 1, 2, 3 };
-    , &.{"-std=c11"});
-    b_o.force_pic = true;
+        ,
+        .c_source_flags = &.{"-std=c11"},
+        .pic = true,
+    });
 
-    const c_o = addObject(b, "c", opts);
-    addCSourceBytes(c_o,
+    const c_o = addObject(b, opts, .{
+        .name = "c",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\extern _Thread_local int x;
         \\extern _Thread_local int y[];
         \\int main() {
         \\  printf("%d %d %d %d\n", x, y[0], y[1], y[2]);
         \\}
-    , &.{});
-    c_o.force_pic = true;
+        ,
+        .pic = true,
+    });
     c_o.linkLibC();
 
     {
-        const dso = addSharedLibrary(b, "a", opts);
+        const dso = addSharedLibrary(b, opts, .{ .name = "a" });
         dso.addObject(a_o);
         dso.addObject(b_o);
 
-        const exe = addExecutable(b, "main", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main" });
         exe.addObject(c_o);
         exe.linkLibrary(dso);
         exe.linkLibC();
@@ -2894,7 +3071,7 @@ fn testTlsLargeAlignment(b: *Build, opts: Options) *Step {
     }
 
     {
-        const exe = addExecutable(b, "main", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main" });
         exe.addObject(a_o);
         exe.addObject(b_o);
         exe.addObject(c_o);
@@ -2913,7 +3090,7 @@ fn testTlsLargeAlignment(b: *Build, opts: Options) *Step {
 fn testTlsLargeTbss(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-large-tbss", opts);
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addAsmSourceBytes(exe,
         \\.globl x, y
         \\.section .tbss,"awT",@nobits
@@ -2947,7 +3124,7 @@ fn testTlsLargeTbss(b: *Build, opts: Options) *Step {
 fn testTlsLargeStaticImage(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-large-static-image", opts);
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe, "_Thread_local int x[] = { 1, 2, 3, [10000] = 5 };", &.{});
     addCSourceBytes(exe,
         \\#include <stdio.h>
@@ -2956,7 +3133,7 @@ fn testTlsLargeStaticImage(b: *Build, opts: Options) *Step {
         \\  printf("%d %d %d %d %d\n", x[0], x[1], x[2], x[3], x[10000]);
         \\}
     , &.{});
-    exe.force_pic = true;
+    exe.root_module.pic = true;
     exe.linkLibC();
     // https://github.com/ziglang/zig/issues/17619
     exe.pie = true;
@@ -2971,8 +3148,9 @@ fn testTlsLargeStaticImage(b: *Build, opts: Options) *Step {
 fn testTlsLd(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-ld", opts);
 
-    const main_o = addObject(b, "main", opts);
-    addCSourceBytes(main_o,
+    const main_o = addObject(b, opts, .{
+        .name = "main",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\extern _Thread_local int foo;
         \\static _Thread_local int bar;
@@ -2983,18 +3161,23 @@ fn testTlsLd(b: *Build, opts: Options) *Step {
         \\  printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
         \\  return 0;
         \\}
-    , &.{"-ftls-model=local-dynamic"});
-    main_o.force_pic = true;
+        ,
+        .c_source_flags = &.{"-ftls-model=local-dynamic"},
+        .pic = true,
+    });
     main_o.linkLibC();
 
-    const a_o = addObject(b, "a", opts);
-    addCSourceBytes(a_o, "_Thread_local int foo = 3;", &.{"-ftls-model=local-dynamic"});
-    a_o.force_pic = true;
+    const a_o = addObject(b, opts, .{
+        .name = "a",
+        .c_source_bytes = "_Thread_local int foo = 3;",
+        .c_source_flags = &.{"-ftls-model=local-dynamic"},
+        .pic = true,
+    });
 
     const exp_stdout = "3 5 3 5\n";
 
     {
-        const exe = addExecutable(b, "main1", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main1" });
         exe.addObject(main_o);
         exe.addObject(a_o);
         exe.linkLibC();
@@ -3007,7 +3190,7 @@ fn testTlsLd(b: *Build, opts: Options) *Step {
     }
 
     {
-        const exe = addExecutable(b, "main2", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main2" });
         exe.addObject(main_o);
         exe.addObject(a_o);
         exe.linkLibC();
@@ -3026,14 +3209,14 @@ fn testTlsLd(b: *Build, opts: Options) *Step {
 fn testTlsLdDso(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-ld-dso", opts);
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(dso,
         \\static _Thread_local int def, def1;
         \\int f0() { return ++def; }
         \\int f1() { return ++def1 + def; }
     , &.{"-ftls-model=local-dynamic"});
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\#include <stdio.h>
         \\extern int f0();
@@ -3060,8 +3243,9 @@ fn testTlsLdDso(b: *Build, opts: Options) *Step {
 fn testTlsLdNoPlt(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-ld-no-plt", opts);
 
-    const a_o = addObject(b, "a", opts);
-    addCSourceBytes(a_o,
+    const a_o = addObject(b, opts, .{
+        .name = "a",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\extern _Thread_local int foo;
         \\static _Thread_local int bar;
@@ -3073,16 +3257,21 @@ fn testTlsLdNoPlt(b: *Build, opts: Options) *Step {
         \\  printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
         \\  return 0;
         \\}
-    , &.{ "-ftls-model=local-dynamic", "-fno-plt" });
+        ,
+        .c_source_flags = &.{ "-ftls-model=local-dynamic", "-fno-plt" },
+        .pic = true,
+    });
     a_o.linkLibC();
-    a_o.force_pic = true;
 
-    const b_o = addObject(b, "b", opts);
-    addCSourceBytes(b_o, "_Thread_local int foo = 3;", &.{ "-ftls-model=local-dynamic", "-fno-plt" });
-    b_o.force_pic = true;
+    const b_o = addObject(b, opts, .{
+        .name = "b",
+        .c_source_bytes = "_Thread_local int foo = 3;",
+        .c_source_flags = &.{ "-ftls-model=local-dynamic", "-fno-plt" },
+        .pic = true,
+    });
 
     {
-        const exe = addExecutable(b, "main1", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main1" });
         exe.addObject(a_o);
         exe.addObject(b_o);
         exe.linkLibC();
@@ -3095,7 +3284,7 @@ fn testTlsLdNoPlt(b: *Build, opts: Options) *Step {
     }
 
     {
-        const exe = addExecutable(b, "main2", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main2" });
         exe.addObject(a_o);
         exe.addObject(b_o);
         exe.linkLibC();
@@ -3114,7 +3303,7 @@ fn testTlsLdNoPlt(b: *Build, opts: Options) *Step {
 fn testTlsNoPic(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-no-pic", opts);
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\#include <stdio.h>
         \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo;
@@ -3132,7 +3321,7 @@ fn testTlsNoPic(b: *Build, opts: Options) *Step {
     addCSourceBytes(exe,
         \\__attribute__((tls_model("global-dynamic"))) _Thread_local int foo;
     , &.{});
-    exe.force_pic = false;
+    exe.root_module.pic = false;
     exe.linkLibC();
 
     const run = addRunArtifact(exe);
@@ -3145,7 +3334,7 @@ fn testTlsNoPic(b: *Build, opts: Options) *Step {
 fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-offset-alignment", opts);
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(dso,
         \\#include <assert.h>
         \\#include <stdlib.h>
@@ -3163,7 +3352,7 @@ fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step {
     , &.{});
     dso.linkLibC();
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\#include <pthread.h>
         \\#include <dlfcn.h>
@@ -3186,7 +3375,7 @@ fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step {
     , &.{});
     exe.addRPath(dso.getEmittedBinDirectory());
     exe.linkLibC();
-    exe.force_pic = true;
+    exe.root_module.pic = true;
     // https://github.com/ziglang/zig/issues/17619
     exe.pie = true;
 
@@ -3200,8 +3389,9 @@ fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step {
 fn testTlsPic(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-pic", opts);
 
-    const obj = addObject(b, "obj", opts);
-    addCSourceBytes(obj,
+    const obj = addObject(b, opts, .{
+        .name = "obj",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo;
         \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int bar;
@@ -3213,11 +3403,12 @@ fn testTlsPic(b: *Build, opts: Options) *Step {
         \\  printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
         \\  return 0;
         \\}
-    , &.{});
+        ,
+        .pic = true,
+    });
     obj.linkLibC();
-    obj.force_pic = true;
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\__attribute__((tls_model("global-dynamic"))) _Thread_local int foo = 3;
     , &.{});
@@ -3236,30 +3427,38 @@ fn testTlsPic(b: *Build, opts: Options) *Step {
 fn testTlsSmallAlignment(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-small-alignment", opts);
 
-    const a_o = addObject(b, "a", opts);
-    addAsmSourceBytes(a_o,
+    const a_o = addObject(b, opts, .{
+        .name = "a",
+        .asm_source_bytes =
         \\.text
         \\.byte 0
-    );
-    a_o.force_pic = true;
+        \\
+        ,
+        .pic = true,
+    });
 
-    const b_o = addObject(b, "b", opts);
-    addCSourceBytes(b_o, "_Thread_local char x = 42;", &.{"-std=c11"});
-    b_o.force_pic = true;
+    const b_o = addObject(b, opts, .{
+        .name = "b",
+        .c_source_bytes = "_Thread_local char x = 42;",
+        .c_source_flags = &.{"-std=c11"},
+        .pic = true,
+    });
 
-    const c_o = addObject(b, "c", opts);
-    addCSourceBytes(c_o,
+    const c_o = addObject(b, opts, .{
+        .name = "c",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\extern _Thread_local char x;
         \\int main() {
         \\  printf("%d\n", x);
         \\}
-    , &.{});
+        ,
+        .pic = true,
+    });
     c_o.linkLibC();
-    c_o.force_pic = true;
 
     {
-        const exe = addExecutable(b, "main", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main" });
         exe.addObject(a_o);
         exe.addObject(b_o);
         exe.addObject(c_o);
@@ -3273,11 +3472,11 @@ fn testTlsSmallAlignment(b: *Build, opts: Options) *Step {
     }
 
     {
-        const dso = addSharedLibrary(b, "a", opts);
+        const dso = addSharedLibrary(b, opts, .{ .name = "a" });
         dso.addObject(a_o);
         dso.addObject(b_o);
 
-        const exe = addExecutable(b, "main", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main" });
         exe.addObject(c_o);
         exe.linkLibrary(dso);
         exe.linkLibC();
@@ -3295,7 +3494,7 @@ fn testTlsSmallAlignment(b: *Build, opts: Options) *Step {
 fn testTlsStatic(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "tls-static", opts);
 
-    const exe = addExecutable(b, "test", opts);
+    const exe = addExecutable(b, opts, .{ .name = "test" });
     addCSourceBytes(exe,
         \\#include <stdio.h>
         \\_Thread_local int a = 10;
@@ -3326,12 +3525,14 @@ fn testTlsStatic(b: *Build, opts: Options) *Step {
 fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "unknown-file-type-error", opts);
 
-    const dylib = addSharedLibrary(b, "a", .{
-        .target = .{ .cpu_arch = .x86_64, .os_tag = .macos },
+    const dylib = addSharedLibrary(b, .{
+        .target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .macos }),
+    }, .{
+        .name = "a",
+        .zig_source_bytes = "export var foo: i32 = 0;",
     });
-    addZigSourceBytes(dylib, "export var foo: i32 = 0;");
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\extern int foo;
         \\int main() {
@@ -3354,28 +3555,34 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {
 fn testUnresolvedError(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "unresolved-error", opts);
 
-    const obj1 = addObject(b, "a", opts);
-    addCSourceBytes(obj1,
+    const obj1 = addObject(b, opts, .{
+        .name = "a",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\int foo();
         \\int bar() {
         \\  return foo() + 1;
         \\}
-    , &.{"-ffunction-sections"});
+        ,
+        .c_source_flags = &.{"-ffunction-sections"},
+    });
     obj1.linkLibC();
 
-    const obj2 = addObject(b, "b", opts);
-    addCSourceBytes(obj2,
+    const obj2 = addObject(b, opts, .{
+        .name = "b",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\int foo();
         \\int bar();
         \\int main() {
         \\  return foo() + bar();
         \\}
-    , &.{"-ffunction-sections"});
+        ,
+        .c_source_flags = &.{"-ffunction-sections"},
+    });
     obj2.linkLibC();
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     exe.addObject(obj1);
     exe.addObject(obj2);
     exe.linkLibC();
@@ -3392,19 +3599,21 @@ fn testUnresolvedError(b: *Build, opts: Options) *Step {
 fn testWeakExports(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "weak-exports", opts);
 
-    const obj = addObject(b, "obj", opts);
-    addCSourceBytes(obj,
+    const obj = addObject(b, opts, .{
+        .name = "obj",
+        .c_source_bytes =
         \\#include <stdio.h>
         \\__attribute__((weak)) int foo();
         \\int main() {
         \\  printf("%d\n", foo ? foo() : 3);
         \\}
-    , &.{});
+        ,
+        .pic = true,
+    });
     obj.linkLibC();
-    obj.force_pic = true;
 
     {
-        const dso = addSharedLibrary(b, "a", opts);
+        const dso = addSharedLibrary(b, opts, .{ .name = "a" });
         dso.addObject(obj);
         dso.linkLibC();
 
@@ -3415,7 +3624,7 @@ fn testWeakExports(b: *Build, opts: Options) *Step {
     }
 
     {
-        const exe = addExecutable(b, "main", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main" });
         exe.addObject(obj);
         exe.linkLibC();
         // https://github.com/ziglang/zig/issues/17619
@@ -3437,14 +3646,14 @@ fn testWeakExports(b: *Build, opts: Options) *Step {
 fn testWeakUndefsDso(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "weak-undef-dso", opts);
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     addCSourceBytes(dso,
         \\__attribute__((weak)) int foo();
         \\int bar() { return foo ? foo() : -1; }
     , &.{});
 
     {
-        const exe = addExecutable(b, "main", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main" });
         addCSourceBytes(exe,
             \\#include <stdio.h>
             \\int bar();
@@ -3461,7 +3670,7 @@ fn testWeakUndefsDso(b: *Build, opts: Options) *Step {
     }
 
     {
-        const exe = addExecutable(b, "main", opts);
+        const exe = addExecutable(b, opts, .{ .name = "main" });
         addCSourceBytes(exe,
             \\#include <stdio.h>
             \\int foo() { return 5; }
@@ -3484,12 +3693,14 @@ fn testWeakUndefsDso(b: *Build, opts: Options) *Step {
 fn testZNow(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "z-now", opts);
 
-    const obj = addObject(b, "obj", opts);
-    addCSourceBytes(obj, "int main() { return 0; }", &.{});
-    obj.force_pic = true;
+    const obj = addObject(b, opts, .{
+        .name = "obj",
+        .c_source_bytes = "int main() { return 0; }",
+        .pic = true,
+    });
 
     {
-        const dso = addSharedLibrary(b, "a", opts);
+        const dso = addSharedLibrary(b, opts, .{ .name = "a" });
         dso.addObject(obj);
 
         const check = dso.checkObject();
@@ -3499,7 +3710,7 @@ fn testZNow(b: *Build, opts: Options) *Step {
     }
 
     {
-        const dso = addSharedLibrary(b, "a", opts);
+        const dso = addSharedLibrary(b, opts, .{ .name = "a" });
         dso.addObject(obj);
         dso.link_z_lazy = true;
 
@@ -3515,7 +3726,7 @@ fn testZNow(b: *Build, opts: Options) *Step {
 fn testZStackSize(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "z-stack-size", opts);
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe, "int main() { return 0; }", &.{});
     exe.stack_size = 0x800000;
     exe.linkLibC();
@@ -3540,8 +3751,9 @@ fn testZText(b: *Build, opts: Options) *Step {
     // musl supports only a very limited number of text relocations and only in DSOs (and
     // rightly so!).
 
-    const a_o = addObject(b, "a", opts);
-    addAsmSourceBytes(a_o,
+    const a_o = addObject(b, opts, .{
+        .name = "a",
+        .asm_source_bytes =
         \\.globl fn1
         \\fn1:
         \\  sub $8, %rsp
@@ -3549,10 +3761,13 @@ fn testZText(b: *Build, opts: Options) *Step {
         \\  call *%rax
         \\  add $8, %rsp
         \\  ret
-    );
+        \\
+        ,
+    });
 
-    const b_o = addObject(b, "b", opts);
-    addCSourceBytes(b_o,
+    const b_o = addObject(b, opts, .{
+        .name = "b",
+        .c_source_bytes =
         \\int fn1();
         \\int fn2() {
         \\  return 3;
@@ -3561,15 +3776,16 @@ fn testZText(b: *Build, opts: Options) *Step {
         \\int fnn() {
         \\  return fn1();
         \\}
-    , &.{});
-    b_o.force_pic = true;
+        ,
+        .pic = true,
+    });
 
-    const dso = addSharedLibrary(b, "a", opts);
+    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
     dso.addObject(a_o);
     dso.addObject(b_o);
     dso.link_z_notext = true;
 
-    const exe = addExecutable(b, "main", opts);
+    const exe = addExecutable(b, opts, .{ .name = "main" });
     addCSourceBytes(exe,
         \\#include <stdio.h>
         \\int fnn();
@@ -3608,7 +3824,6 @@ const addObject = link.addObject;
 const addRunArtifact = link.addRunArtifact;
 const addSharedLibrary = link.addSharedLibrary;
 const addStaticLibrary = link.addStaticLibrary;
-const addZigSourceBytes = link.addZigSourceBytes;
 const expectLinkErrors = link.expectLinkErrors;
 const link = @import("link.zig");
 const std = @import("std");
test/link/link.zig
@@ -7,62 +7,160 @@ pub fn build(b: *Build) void {
 }
 
 pub const Options = struct {
-    target: CrossTarget,
+    target: std.Build.ResolvedTarget,
     optimize: std.builtin.OptimizeMode = .Debug,
     use_llvm: bool = true,
     use_lld: bool = false,
 };
 
-pub fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
-    const target = opts.target.zigTriple(b.allocator) catch @panic("OOM");
+pub fn addTestStep(b: *Build, prefix: []const u8, opts: Options) *Step {
+    const target = opts.target.target.zigTriple(b.allocator) catch @panic("OOM");
     const optimize = @tagName(opts.optimize);
     const use_llvm = if (opts.use_llvm) "llvm" else "no-llvm";
-    const name = std.fmt.allocPrint(b.allocator, "test-" ++ prefix ++ "-{s}-{s}-{s}", .{
-        target,
-        optimize,
-        use_llvm,
+    const name = std.fmt.allocPrint(b.allocator, "test-{s}-{s}-{s}-{s}", .{
+        prefix, target, optimize, use_llvm,
     }) catch @panic("OOM");
     return b.step(name, "");
 }
 
-pub fn addExecutable(b: *Build, name: []const u8, opts: Options) *Compile {
-    return b.addExecutable(.{
-        .name = name,
-        .target = opts.target,
-        .optimize = opts.optimize,
-        .use_llvm = opts.use_llvm,
-        .use_lld = opts.use_lld,
+const OverlayOptions = struct {
+    name: []const u8,
+    asm_source_bytes: ?[]const u8 = null,
+    c_source_bytes: ?[]const u8 = null,
+    c_source_flags: []const []const u8 = &.{},
+    cpp_source_bytes: ?[]const u8 = null,
+    cpp_source_flags: []const []const u8 = &.{},
+    zig_source_bytes: ?[]const u8 = null,
+    pic: ?bool = null,
+    strip: ?bool = null,
+};
+
+pub fn addExecutable(b: *std.Build, base: Options, overlay: OverlayOptions) *Step.Compile {
+    const compile_step = b.addExecutable(.{
+        .name = overlay.name,
+        .root_source_file = rsf: {
+            const bytes = overlay.zig_source_bytes orelse break :rsf null;
+            break :rsf b.addWriteFiles().add("a.zig", bytes);
+        },
+        .target = base.target,
+        .optimize = base.optimize,
+        .use_llvm = base.use_llvm,
+        .use_lld = base.use_lld,
+        .pic = overlay.pic,
+        .strip = overlay.strip,
     });
+    if (overlay.cpp_source_bytes) |bytes| {
+        compile_step.addCSourceFile(.{
+            .file = b.addWriteFiles().add("a.cpp", bytes),
+            .flags = overlay.cpp_source_flags,
+        });
+    }
+    if (overlay.c_source_bytes) |bytes| {
+        compile_step.addCSourceFile(.{
+            .file = b.addWriteFiles().add("a.c", bytes),
+            .flags = overlay.c_source_flags,
+        });
+    }
+    if (overlay.asm_source_bytes) |bytes| {
+        compile_step.addAssemblyFile(b.addWriteFiles().add("a.s", bytes));
+    }
+    return compile_step;
 }
 
-pub fn addObject(b: *Build, name: []const u8, opts: Options) *Compile {
-    return b.addObject(.{
-        .name = name,
-        .target = opts.target,
-        .optimize = opts.optimize,
-        .use_llvm = opts.use_llvm,
-        .use_lld = opts.use_lld,
+pub fn addObject(b: *Build, base: Options, overlay: OverlayOptions) *Step.Compile {
+    const compile_step = b.addObject(.{
+        .name = overlay.name,
+        .root_source_file = rsf: {
+            const bytes = overlay.zig_source_bytes orelse break :rsf null;
+            break :rsf b.addWriteFiles().add("a.zig", bytes);
+        },
+        .target = base.target,
+        .optimize = base.optimize,
+        .use_llvm = base.use_llvm,
+        .use_lld = base.use_lld,
+        .pic = overlay.pic,
+        .strip = overlay.strip,
     });
+    if (overlay.cpp_source_bytes) |bytes| {
+        compile_step.addCSourceFile(.{
+            .file = b.addWriteFiles().add("a.cpp", bytes),
+            .flags = overlay.cpp_source_flags,
+        });
+    }
+    if (overlay.c_source_bytes) |bytes| {
+        compile_step.addCSourceFile(.{
+            .file = b.addWriteFiles().add("a.c", bytes),
+            .flags = overlay.c_source_flags,
+        });
+    }
+    if (overlay.asm_source_bytes) |bytes| {
+        compile_step.addAssemblyFile(b.addWriteFiles().add("a.s", bytes));
+    }
+    return compile_step;
 }
 
-pub fn addStaticLibrary(b: *Build, name: []const u8, opts: Options) *Compile {
-    return b.addStaticLibrary(.{
-        .name = name,
-        .target = opts.target,
-        .optimize = opts.optimize,
-        .use_llvm = opts.use_llvm,
-        .use_lld = opts.use_lld,
+pub fn addStaticLibrary(b: *Build, base: Options, overlay: OverlayOptions) *Compile {
+    const compile_step = b.addStaticLibrary(.{
+        .name = overlay.name,
+        .root_source_file = rsf: {
+            const bytes = overlay.zig_source_bytes orelse break :rsf null;
+            break :rsf b.addWriteFiles().add("a.zig", bytes);
+        },
+        .target = base.target,
+        .optimize = base.optimize,
+        .use_llvm = base.use_llvm,
+        .use_lld = base.use_lld,
+        .pic = overlay.pic,
+        .strip = overlay.strip,
     });
+    if (overlay.cpp_source_bytes) |bytes| {
+        compile_step.addCSourceFile(.{
+            .file = b.addWriteFiles().add("a.cpp", bytes),
+            .flags = overlay.cpp_source_flags,
+        });
+    }
+    if (overlay.c_source_bytes) |bytes| {
+        compile_step.addCSourceFile(.{
+            .file = b.addWriteFiles().add("a.c", bytes),
+            .flags = overlay.c_source_flags,
+        });
+    }
+    if (overlay.asm_source_bytes) |bytes| {
+        compile_step.addAssemblyFile(b.addWriteFiles().add("a.s", bytes));
+    }
+    return compile_step;
 }
 
-pub fn addSharedLibrary(b: *Build, name: []const u8, opts: Options) *Compile {
-    return b.addSharedLibrary(.{
-        .name = name,
-        .target = opts.target,
-        .optimize = opts.optimize,
-        .use_llvm = opts.use_llvm,
-        .use_lld = opts.use_lld,
+pub fn addSharedLibrary(b: *Build, base: Options, overlay: OverlayOptions) *Compile {
+    const compile_step = b.addSharedLibrary(.{
+        .name = overlay.name,
+        .root_source_file = rsf: {
+            const bytes = overlay.zig_source_bytes orelse break :rsf null;
+            break :rsf b.addWriteFiles().add("a.zig", bytes);
+        },
+        .target = base.target,
+        .optimize = base.optimize,
+        .use_llvm = base.use_llvm,
+        .use_lld = base.use_lld,
+        .pic = overlay.pic,
+        .strip = overlay.strip,
     });
+    if (overlay.cpp_source_bytes) |bytes| {
+        compile_step.addCSourceFile(.{
+            .file = b.addWriteFiles().add("a.cpp", bytes),
+            .flags = overlay.cpp_source_flags,
+        });
+    }
+    if (overlay.c_source_bytes) |bytes| {
+        compile_step.addCSourceFile(.{
+            .file = b.addWriteFiles().add("a.c", bytes),
+            .flags = overlay.c_source_flags,
+        });
+    }
+    if (overlay.asm_source_bytes) |bytes| {
+        compile_step.addAssemblyFile(b.addWriteFiles().add("a.s", bytes));
+    }
+    return compile_step;
 }
 
 pub fn addRunArtifact(comp: *Compile) *Run {
@@ -72,13 +170,6 @@ pub fn addRunArtifact(comp: *Compile) *Run {
     return run;
 }
 
-pub fn addZigSourceBytes(comp: *Compile, bytes: []const u8) void {
-    const b = comp.step.owner;
-    const file = WriteFile.create(b).add("a.zig", bytes);
-    file.addStepDependencies(&comp.step);
-    comp.root_src = file;
-}
-
 pub fn addCSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void {
     const b = comp.step.owner;
     const file = WriteFile.create(b).add("a.c", bytes);
test/link/macho.zig
@@ -1,26 +1,29 @@
 //! Here we test our MachO linker for correctness and functionality.
 //! TODO migrate standalone tests from test/link/macho/* to here.
 
-pub fn testAll(b: *Build) *Step {
+pub fn testAll(b: *std.Build) *Step {
     const macho_step = b.step("test-macho", "Run MachO tests");
 
-    const default_target = CrossTarget{ .os_tag = .macos };
-
-    macho_step.dependOn(testResolvingBoundarySymbols(b, .{ .target = default_target }));
+    macho_step.dependOn(testResolvingBoundarySymbols(b, .{
+        .target = b.resolveTargetQuery(.{ .os_tag = .macos }),
+    }));
 
     return macho_step;
 }
 
-fn testResolvingBoundarySymbols(b: *Build, opts: Options) *Step {
+fn testResolvingBoundarySymbols(b: *std.Build, opts: Options) *Step {
     const test_step = addTestStep(b, "macho-resolving-boundary-symbols", opts);
 
-    const obj1 = addObject(b, "obj1", opts);
-    addCppSourceBytes(obj1,
+    const obj1 = addObject(b, opts, .{
+        .name = "obj1",
+        .cpp_source_bytes =
         \\constexpr const char* MESSAGE __attribute__((used, section("__DATA_CONST,__message_ptr"))) = "codebase";
-    , &.{});
+        ,
+    });
 
-    const main_o = addObject(b, "main", opts);
-    addZigSourceBytes(main_o,
+    const main_o = addObject(b, opts, .{
+        .name = "main",
+        .zig_source_bytes =
         \\const std = @import("std");
         \\extern fn interop() [*:0]const u8;
         \\pub fn main() !void {
@@ -28,23 +31,27 @@ fn testResolvingBoundarySymbols(b: *Build, opts: Options) *Step {
         \\        std.mem.span(interop()),
         \\    });
         \\}
-    );
+        ,
+    });
 
     {
-        const obj2 = addObject(b, "obj2", opts);
-        addCppSourceBytes(obj2,
+        const obj2 = addObject(b, opts, .{
+            .name = "obj2",
+            .cpp_source_bytes =
             \\extern const char* message_pointer __asm("section$start$__DATA_CONST$__message_ptr");
             \\extern "C" const char* interop() {
             \\  return message_pointer;
             \\}
-        , &.{});
+            ,
+        });
 
-        const exe = addExecutable(b, "test", opts);
+        const exe = addExecutable(b, opts, .{ .name = "test" });
         exe.addObject(obj1);
         exe.addObject(obj2);
         exe.addObject(main_o);
 
-        const run = addRunArtifact(exe);
+        const run = b.addRunArtifact(exe);
+        run.skip_foreign_checks = true;
         run.expectStdErrEqual("All your codebase are belong to us.\n");
         test_step.dependOn(&run.step);
 
@@ -55,15 +62,17 @@ fn testResolvingBoundarySymbols(b: *Build, opts: Options) *Step {
     }
 
     {
-        const obj3 = addObject(b, "obj3", opts);
-        addCppSourceBytes(obj3,
+        const obj3 = addObject(b, opts, .{
+            .name = "obj3",
+            .cpp_source_bytes =
             \\extern const char* message_pointer __asm("section$start$__DATA$__message_ptr");
             \\extern "C" const char* interop() {
             \\  return message_pointer;
             \\}
-        , &.{});
+            ,
+        });
 
-        const exe = addExecutable(b, "test", opts);
+        const exe = addExecutable(b, opts, .{ .name = "test" });
         exe.addObject(obj1);
         exe.addObject(obj3);
         exe.addObject(main_o);
@@ -77,20 +86,16 @@ fn testResolvingBoundarySymbols(b: *Build, opts: Options) *Step {
     return test_step;
 }
 
-fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
+fn addTestStep(b: *std.Build, comptime prefix: []const u8, opts: Options) *Step {
     return link.addTestStep(b, "macho-" ++ prefix, opts);
 }
 
-const addCppSourceBytes = link.addCppSourceBytes;
-const addExecutable = link.addExecutable;
 const addObject = link.addObject;
-const addRunArtifact = link.addRunArtifact;
-const addZigSourceBytes = link.addZigSourceBytes;
+const addExecutable = link.addExecutable;
 const expectLinkErrors = link.expectLinkErrors;
 const link = @import("link.zig");
 const std = @import("std");
 
-const Build = std.Build;
 const CrossTarget = std.zig.CrossTarget;
 const Options = link.Options;
-const Step = Build.Step;
+const Step = std.Build.Step;
test/src/Cases.zig
@@ -76,7 +76,7 @@ pub const Case = struct {
     name: []const u8,
     /// The platform the test targets. For non-native platforms, an emulator
     /// such as QEMU is required for tests to complete.
-    target: CrossTarget,
+    target: std.Build.ResolvedTarget,
     /// In order to be able to run e.g. Execution updates, this must be set
     /// to Executable.
     output_mode: std.builtin.OutputMode,
@@ -155,7 +155,7 @@ pub const Translate = struct {
     name: []const u8,
 
     input: [:0]const u8,
-    target: CrossTarget,
+    target: std.Build.ResolvedTarget,
     link_libc: bool,
     c_frontend: CFrontend,
     kind: union(enum) {
@@ -171,7 +171,7 @@ pub const Translate = struct {
 pub fn addExe(
     ctx: *Cases,
     name: []const u8,
-    target: CrossTarget,
+    target: std.Build.ResolvedTarget,
 ) *Case {
     ctx.cases.append(Case{
         .name = name,
@@ -184,16 +184,16 @@ pub fn addExe(
 }
 
 /// Adds a test case for Zig input, producing an executable
-pub fn exe(ctx: *Cases, name: []const u8, target: CrossTarget) *Case {
+pub fn exe(ctx: *Cases, name: []const u8, target: std.Build.ResolvedTarget) *Case {
     return ctx.addExe(name, target);
 }
 
-pub fn exeFromCompiledC(ctx: *Cases, name: []const u8, target: CrossTarget) *Case {
-    var target_adjusted = target;
-    target_adjusted.ofmt = .c;
+pub fn exeFromCompiledC(ctx: *Cases, name: []const u8, target_query: std.zig.CrossTarget, b: *std.Build) *Case {
+    var adjusted_query = target_query;
+    adjusted_query.ofmt = .c;
     ctx.cases.append(Case{
         .name = name,
-        .target = target_adjusted,
+        .target = b.resolveTargetQuery(adjusted_query),
         .updates = std.ArrayList(Update).init(ctx.cases.allocator),
         .output_mode = .Exe,
         .deps = std.ArrayList(DepModule).init(ctx.arena),
@@ -202,7 +202,7 @@ pub fn exeFromCompiledC(ctx: *Cases, name: []const u8, target: CrossTarget) *Cas
     return &ctx.cases.items[ctx.cases.items.len - 1];
 }
 
-pub fn noEmitUsingLlvmBackend(ctx: *Cases, name: []const u8, target: CrossTarget) *Case {
+pub fn noEmitUsingLlvmBackend(ctx: *Cases, name: []const u8, target: std.Build.ResolvedTarget) *Case {
     ctx.cases.append(Case{
         .name = name,
         .target = target,
@@ -217,7 +217,7 @@ pub fn noEmitUsingLlvmBackend(ctx: *Cases, name: []const u8, target: CrossTarget
 
 /// Adds a test case that uses the LLVM backend to emit an executable.
 /// Currently this implies linking libc, because only then we can generate a testable executable.
-pub fn exeUsingLlvmBackend(ctx: *Cases, name: []const u8, target: CrossTarget) *Case {
+pub fn exeUsingLlvmBackend(ctx: *Cases, name: []const u8, target: std.Build.ResolvedTarget) *Case {
     ctx.cases.append(Case{
         .name = name,
         .target = target,
@@ -233,7 +233,7 @@ pub fn exeUsingLlvmBackend(ctx: *Cases, name: []const u8, target: CrossTarget) *
 pub fn addObj(
     ctx: *Cases,
     name: []const u8,
-    target: CrossTarget,
+    target: std.Build.ResolvedTarget,
 ) *Case {
     ctx.cases.append(Case{
         .name = name,
@@ -248,7 +248,7 @@ pub fn addObj(
 pub fn addTest(
     ctx: *Cases,
     name: []const u8,
-    target: CrossTarget,
+    target: std.Build.ResolvedTarget,
 ) *Case {
     ctx.cases.append(Case{
         .name = name,
@@ -262,17 +262,17 @@ pub fn addTest(
 }
 
 /// Adds a test case for Zig input, producing an object file.
-pub fn obj(ctx: *Cases, name: []const u8, target: CrossTarget) *Case {
+pub fn obj(ctx: *Cases, name: []const u8, target: std.Build.ResolvedTarget) *Case {
     return ctx.addObj(name, target);
 }
 
 /// Adds a test case for ZIR input, producing an object file.
-pub fn objZIR(ctx: *Cases, name: []const u8, target: CrossTarget) *Case {
+pub fn objZIR(ctx: *Cases, name: []const u8, target: std.Build.ResolvedTarget) *Case {
     return ctx.addObj(name, target, .ZIR);
 }
 
 /// Adds a test case for Zig or ZIR input, producing C code.
-pub fn addC(ctx: *Cases, name: []const u8, target: CrossTarget) *Case {
+pub fn addC(ctx: *Cases, name: []const u8, target: std.Build.ResolvedTarget) *Case {
     var target_adjusted = target;
     target_adjusted.ofmt = std.Target.ObjectFormat.c;
     ctx.cases.append(Case{
@@ -308,7 +308,7 @@ pub fn compareOutput(
 pub fn addTransform(
     ctx: *Cases,
     name: []const u8,
-    target: CrossTarget,
+    target: std.Build.ResolvedTarget,
     src: [:0]const u8,
     result: [:0]const u8,
 ) void {
@@ -320,7 +320,7 @@ pub fn addTransform(
 pub fn transform(
     ctx: *Cases,
     name: []const u8,
-    target: CrossTarget,
+    target: std.Build.ResolvedTarget,
     src: [:0]const u8,
     result: [:0]const u8,
 ) void {
@@ -330,7 +330,7 @@ pub fn transform(
 pub fn addError(
     ctx: *Cases,
     name: []const u8,
-    target: CrossTarget,
+    target: std.Build.ResolvedTarget,
     src: [:0]const u8,
     expected_errors: []const []const u8,
 ) void {
@@ -343,7 +343,7 @@ pub fn addError(
 pub fn compileError(
     ctx: *Cases,
     name: []const u8,
-    target: CrossTarget,
+    target: std.Build.ResolvedTarget,
     src: [:0]const u8,
     expected_errors: []const []const u8,
 ) void {
@@ -355,7 +355,7 @@ pub fn compileError(
 pub fn addCompile(
     ctx: *Cases,
     name: []const u8,
-    target: CrossTarget,
+    target: std.Build.ResolvedTarget,
     src: [:0]const u8,
 ) void {
     ctx.addObj(name, target).addCompile(src);
@@ -368,9 +368,9 @@ 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: std.fs.Dir) void {
+pub fn addFromDir(ctx: *Cases, dir: std.fs.Dir, b: *std.Build) void {
     var current_file: []const u8 = "none";
-    ctx.addFromDirInner(dir, &current_file) catch |err| {
+    ctx.addFromDirInner(dir, &current_file, b) catch |err| {
         std.debug.panicExtra(
             @errorReturnTrace(),
             @returnAddress(),
@@ -386,6 +386,7 @@ fn addFromDirInner(
     /// 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,
+    b: *std.Build,
 ) !void {
     var it = try iterable_dir.walk(ctx.arena);
     var filenames = std.ArrayList([]const u8).init(ctx.arena);
@@ -422,7 +423,7 @@ fn addFromDirInner(
         var manifest = try TestManifest.parse(ctx.arena, src);
 
         const backends = try manifest.getConfigForKeyAlloc(ctx.arena, "backend", Backend);
-        const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", CrossTarget);
+        const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", std.zig.CrossTarget);
         const c_frontends = try manifest.getConfigForKeyAlloc(ctx.arena, "c_frontend", CFrontend);
         const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool);
         const link_libc = try manifest.getConfigForKeyAssertSingle("link_libc", bool);
@@ -430,12 +431,12 @@ fn addFromDirInner(
 
         if (manifest.type == .translate_c) {
             for (c_frontends) |c_frontend| {
-                for (targets) |target| {
+                for (targets) |target_query| {
                     const output = try manifest.trailingLinesSplit(ctx.arena);
                     try ctx.translate.append(.{
                         .name = std.fs.path.stem(filename),
                         .c_frontend = c_frontend,
-                        .target = target,
+                        .target = b.resolveTargetQuery(target_query),
                         .link_libc = link_libc,
                         .input = src,
                         .kind = .{ .translate = output },
@@ -446,12 +447,12 @@ fn addFromDirInner(
         }
         if (manifest.type == .run_translated_c) {
             for (c_frontends) |c_frontend| {
-                for (targets) |target| {
+                for (targets) |target_query| {
                     const output = try manifest.trailingSplit(ctx.arena);
                     try ctx.translate.append(.{
                         .name = std.fs.path.stem(filename),
                         .c_frontend = c_frontend,
-                        .target = target,
+                        .target = b.resolveTargetQuery(target_query),
                         .link_libc = link_libc,
                         .input = src,
                         .kind = .{ .run = output },
@@ -464,16 +465,18 @@ fn addFromDirInner(
         var cases = std.ArrayList(usize).init(ctx.arena);
 
         // Cross-product to get all possible test combinations
-        for (backends) |backend| {
-            for (targets) |target| {
+        for (targets) |target_query| {
+            const resolved_target = b.resolveTargetQuery(target_query);
+            const target = resolved_target.target;
+            for (backends) |backend| {
                 if (backend == .stage2 and
-                    target.getCpuArch() != .wasm32 and target.getCpuArch() != .x86_64)
+                    target.cpu.arch != .wasm32 and target.cpu.arch != .x86_64)
                 {
                     // Other backends don't support new liveness format
                     continue;
                 }
-                if (backend == .stage2 and target.getOsTag() == .macos and
-                    target.getCpuArch() == .x86_64 and builtin.cpu.arch == .aarch64)
+                if (backend == .stage2 and target.os.tag == .macos and
+                    target.cpu.arch == .x86_64 and builtin.cpu.arch == .aarch64)
                 {
                     // Rosetta has issues with ZLD
                     continue;
@@ -482,7 +485,7 @@ fn addFromDirInner(
                 const next = ctx.cases.items.len;
                 try ctx.cases.append(.{
                     .name = std.fs.path.stem(filename),
-                    .target = target,
+                    .target = resolved_target,
                     .backend = backend,
                     .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator),
                     .is_test = is_test,
@@ -622,8 +625,8 @@ pub fn lowerToBuildSteps(
         }
 
         for (case.deps.items) |dep| {
-            artifact.addAnonymousModule(dep.name, .{
-                .source_file = file_sources.get(dep.path).?,
+            artifact.root_module.addAnonymousImport(dep.name, .{
+                .root_source_file = file_sources.get(dep.path).?,
             });
         }
 
@@ -644,9 +647,8 @@ pub fn lowerToBuildSteps(
                 parent_step.dependOn(&artifact.step);
             },
             .Execution => |expected_stdout| no_exec: {
-                const run = if (case.target.ofmt == .c) run_step: {
-                    const target_info = std.zig.system.NativeTargetInfo.detect(case.target) catch |err|
-                        std.debug.panic("unable to detect target host: {s}\n", .{@errorName(err)});
+                const run = if (case.target.target.ofmt == .c) run_step: {
+                    const target_info = case.target.toNativeTargetInfo();
                     if (host.getExternalExecutor(&target_info, .{ .link_libc = true }) != .native) {
                         // We wouldn't be able to run the compiled C code.
                         break :no_exec;
@@ -666,7 +668,7 @@ pub fn lowerToBuildSteps(
                         "--",
                         "-lc",
                         "-target",
-                        case.target.zigTriple(b.allocator) catch @panic("OOM"),
+                        case.target.target.zigTriple(b.allocator) catch @panic("OOM"),
                     });
                     run_c.addArtifactArg(artifact);
                     break :run_step run_c;
@@ -692,8 +694,7 @@ pub fn lowerToBuildSteps(
                 continue; // Pass test.
             }
 
-            const target_info = std.zig.system.NativeTargetInfo.detect(case.target) catch |err|
-                std.debug.panic("unable to detect target host: {s}\n", .{@errorName(err)});
+            const target_info = case.target.toNativeTargetInfo();
             if (host.getExternalExecutor(&target_info, .{ .link_libc = true }) != .native) {
                 // We wouldn't be able to run the compiled C code.
                 continue; // Pass test.
@@ -1159,9 +1160,9 @@ const TestManifest = struct {
     }
 
     fn getDefaultParser(comptime T: type) ParseFn(T) {
-        if (T == CrossTarget) return struct {
+        if (T == std.zig.CrossTarget) return struct {
             fn parse(str: []const u8) anyerror!T {
-                return CrossTarget.parse(.{ .arch_os_abi = str });
+                return std.zig.CrossTarget.parse(.{ .arch_os_abi = str });
             }
         }.parse;
 
@@ -1198,7 +1199,6 @@ const builtin = @import("builtin");
 const std = @import("std");
 const assert = std.debug.assert;
 const Allocator = std.mem.Allocator;
-const CrossTarget = std.zig.CrossTarget;
 const Compilation = @import("../../src/Compilation.zig");
 const zig_h = @import("../../src/link.zig").File.C.zig_h;
 const introspect = @import("../../src/introspect.zig");
@@ -1287,7 +1287,7 @@ pub fn main() !void {
 
             if (cases.items.len == 0) {
                 const backends = try manifest.getConfigForKeyAlloc(arena, "backend", Backend);
-                const targets = try manifest.getConfigForKeyAlloc(arena, "target", CrossTarget);
+                const targets = try manifest.getConfigForKeyAlloc(arena, "target", std.zig.CrossTarget);
                 const c_frontends = try manifest.getConfigForKeyAlloc(ctx.arena, "c_frontend", CFrontend);
                 const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool);
                 const link_libc = try manifest.getConfigForKeyAssertSingle("link_libc", bool);
@@ -1295,12 +1295,12 @@ pub fn main() !void {
 
                 if (manifest.type == .translate_c) {
                     for (c_frontends) |c_frontend| {
-                        for (targets) |target| {
+                        for (targets) |target_query| {
                             const output = try manifest.trailingLinesSplit(ctx.arena);
                             try ctx.translate.append(.{
                                 .name = std.fs.path.stem(filename),
                                 .c_frontend = c_frontend,
-                                .target = target,
+                                .target = resolveTargetQuery(target_query),
                                 .is_test = is_test,
                                 .link_libc = link_libc,
                                 .input = src,
@@ -1312,12 +1312,12 @@ pub fn main() !void {
                 }
                 if (manifest.type == .run_translated_c) {
                     for (c_frontends) |c_frontend| {
-                        for (targets) |target| {
+                        for (targets) |target_query| {
                             const output = try manifest.trailingSplit(ctx.arena);
                             try ctx.translate.append(.{
                                 .name = std.fs.path.stem(filename),
                                 .c_frontend = c_frontend,
-                                .target = target,
+                                .target = resolveTargetQuery(target_query),
                                 .is_test = is_test,
                                 .link_libc = link_libc,
                                 .output = output,
@@ -1385,6 +1385,17 @@ pub fn main() !void {
     return runCases(&ctx, zig_exe_path);
 }
 
+fn resolveTargetQuery(query: std.zig.CrossTarget) std.Build.ResolvedTarget {
+    const result = std.zig.system.NativeTargetInfo.detect(query) catch
+        @panic("unable to resolve target query");
+
+    return .{
+        .query = query,
+        .target = result.target,
+        .dynamic_linker = result.dynamic_linker,
+    };
+}
+
 fn runCases(self: *Cases, zig_exe_path: []const u8) !void {
     const host = try std.zig.system.NativeTargetInfo.detect(.{});
 
test/src/CompareOutput.zig
@@ -96,7 +96,7 @@ pub fn addCase(self: *CompareOutput, case: TestCase) void {
 
             const exe = b.addExecutable(.{
                 .name = "test",
-                .target = .{},
+                .target = b.host,
                 .optimize = .Debug,
             });
             exe.addAssemblyFile(write_src.files.items[0].getPath());
@@ -121,7 +121,7 @@ pub fn addCase(self: *CompareOutput, case: TestCase) void {
                     .name = "test",
                     .root_source_file = write_src.files.items[0].getPath(),
                     .optimize = optimize,
-                    .target = .{},
+                    .target = b.host,
                 });
                 if (case.link_libc) {
                     exe.linkSystemLibrary("c");
@@ -146,7 +146,7 @@ pub fn addCase(self: *CompareOutput, case: TestCase) void {
             const exe = b.addExecutable(.{
                 .name = "test",
                 .root_source_file = write_src.files.items[0].getPath(),
-                .target = .{},
+                .target = b.host,
                 .optimize = .Debug,
             });
             if (case.link_libc) {
test/src/run_translated_c.zig
@@ -11,7 +11,7 @@ pub const RunTranslatedCContext = struct {
     step: *std.Build.Step,
     test_index: usize,
     test_filter: ?[]const u8,
-    target: std.zig.CrossTarget,
+    target: std.Build.ResolvedTarget,
 
     const TestCase = struct {
         name: []const u8,
@@ -86,7 +86,7 @@ pub const RunTranslatedCContext = struct {
         }
         const translate_c = b.addTranslateC(.{
             .source_file = write_src.files.items[0].getPath(),
-            .target = .{},
+            .target = b.host,
             .optimize = .Debug,
         });
 
test/src/StackTrace.zig
@@ -77,7 +77,7 @@ fn addExpect(
         .name = "test",
         .root_source_file = write_src.files.items[0].getPath(),
         .optimize = optimize_mode,
-        .target = .{},
+        .target = b.host,
     });
 
     const run = b.addRunArtifact(exe);
test/src/translate_c.zig
@@ -18,7 +18,7 @@ pub const TranslateCContext = struct {
         sources: ArrayList(SourceFile),
         expected_lines: ArrayList([]const u8),
         allow_warnings: bool,
-        target: CrossTarget = CrossTarget{},
+        target: CrossTarget = .{},
 
         const SourceFile = struct {
             filename: []const u8,
@@ -109,7 +109,7 @@ pub const TranslateCContext = struct {
 
         const translate_c = b.addTranslateC(.{
             .source_file = write_src.files.items[0].getPath(),
-            .target = case.target,
+            .target = b.resolveTargetQuery(case.target),
             .optimize = .Debug,
         });
 
test/standalone/c_compiler/build.zig
@@ -23,7 +23,7 @@ fn add(
     cpp_name: []const u8,
     optimize: std.builtin.OptimizeMode,
 ) void {
-    const target: std.zig.CrossTarget = .{};
+    const target = b.host;
 
     const exe_c = b.addExecutable(.{
         .name = c_name,
@@ -42,7 +42,7 @@ fn add(
     exe_cpp.addCSourceFile(.{ .file = .{ .path = "test.cpp" }, .flags = &[0][]const u8{} });
     exe_cpp.linkLibCpp();
 
-    switch (target.getOsTag()) {
+    switch (target.target.os.tag) {
         .windows => {
             // https://github.com/ziglang/zig/issues/8531
             exe_cpp.want_lto = false;
test/standalone/child_process/build.zig
@@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void {
     b.default_step = test_step;
 
     const optimize: std.builtin.OptimizeMode = .Debug;
-    const target: std.zig.CrossTarget = .{};
+    const target = b.host;
 
     if (builtin.os.tag == .wasi) return;
 
test/standalone/compiler_rt_panic/build.zig
@@ -4,16 +4,16 @@ pub fn build(b: *std.Build) void {
     const test_step = b.step("test", "Test it");
     b.default_step = test_step;
 
-    const target = b.standardTargetOptions(.{});
+    const resolved_target = b.standardTargetOptions(.{});
+    const target = resolved_target.target;
     const optimize = b.standardOptimizeOption(.{});
 
-    const abi = target.getAbi();
-    if (target.getObjectFormat() != .elf or !(abi.isMusl() or abi.isGnu())) return;
+    if (target.ofmt != .elf or !(target.abi.isMusl() or target.abi.isGnu())) return;
 
     const exe = b.addExecutable(.{
         .name = "main",
         .optimize = optimize,
-        .target = target,
+        .target = resolved_target,
     });
     exe.linkLibC();
     exe.addCSourceFile(.{
test/standalone/dep_diamond/build.zig
@@ -7,21 +7,22 @@ pub fn build(b: *std.Build) void {
     const optimize: std.builtin.OptimizeMode = .Debug;
 
     const shared = b.createModule(.{
-        .source_file = .{ .path = "shared.zig" },
+        .root_source_file = .{ .path = "shared.zig" },
     });
 
     const exe = b.addExecutable(.{
         .name = "test",
         .root_source_file = .{ .path = "test.zig" },
+        .target = b.host,
         .optimize = optimize,
     });
-    exe.addAnonymousModule("foo", .{
-        .source_file = .{ .path = "foo.zig" },
-        .dependencies = &.{.{ .name = "shared", .module = shared }},
+    exe.root_module.addAnonymousImport("foo", .{
+        .root_source_file = .{ .path = "foo.zig" },
+        .imports = &.{.{ .name = "shared", .module = shared }},
     });
-    exe.addAnonymousModule("bar", .{
-        .source_file = .{ .path = "bar.zig" },
-        .dependencies = &.{.{ .name = "shared", .module = shared }},
+    exe.root_module.addAnonymousImport("bar", .{
+        .root_source_file = .{ .path = "bar.zig" },
+        .imports = &.{.{ .name = "shared", .module = shared }},
     });
 
     const run = b.addRunArtifact(exe);
test/standalone/dep_mutually_recursive/build.zig
@@ -7,20 +7,21 @@ pub fn build(b: *std.Build) void {
     const optimize: std.builtin.OptimizeMode = .Debug;
 
     const foo = b.createModule(.{
-        .source_file = .{ .path = "foo.zig" },
+        .root_source_file = .{ .path = "foo.zig" },
     });
     const bar = b.createModule(.{
-        .source_file = .{ .path = "bar.zig" },
+        .root_source_file = .{ .path = "bar.zig" },
     });
-    foo.dependencies.put("bar", bar) catch @panic("OOM");
-    bar.dependencies.put("foo", foo) catch @panic("OOM");
+    foo.addImport("bar", bar);
+    bar.addImport("foo", foo);
 
     const exe = b.addExecutable(.{
         .name = "test",
         .root_source_file = .{ .path = "test.zig" },
+        .target = b.host,
         .optimize = optimize,
     });
-    exe.addModule("foo", foo);
+    exe.root_module.addImport("foo", foo);
 
     const run = b.addRunArtifact(exe);
     test_step.dependOn(&run.step);
test/standalone/dep_recursive/build.zig
@@ -7,16 +7,17 @@ pub fn build(b: *std.Build) void {
     const optimize: std.builtin.OptimizeMode = .Debug;
 
     const foo = b.createModule(.{
-        .source_file = .{ .path = "foo.zig" },
+        .root_source_file = .{ .path = "foo.zig" },
     });
-    foo.dependencies.put("foo", foo) catch @panic("OOM");
+    foo.addImport("foo", foo);
 
     const exe = b.addExecutable(.{
         .name = "test",
         .root_source_file = .{ .path = "test.zig" },
+        .target = b.host,
         .optimize = optimize,
     });
-    exe.addModule("foo", foo);
+    exe.root_module.addImport("foo", foo);
 
     const run = b.addRunArtifact(exe);
     test_step.dependOn(&run.step);
test/standalone/dep_shared_builtin/build.zig
@@ -9,10 +9,11 @@ pub fn build(b: *std.Build) void {
     const exe = b.addExecutable(.{
         .name = "test",
         .root_source_file = .{ .path = "test.zig" },
+        .target = b.host,
         .optimize = optimize,
     });
-    exe.addAnonymousModule("foo", .{
-        .source_file = .{ .path = "foo.zig" },
+    exe.root_module.addAnonymousImport("foo", .{
+        .root_source_file = .{ .path = "foo.zig" },
     });
 
     const run = b.addRunArtifact(exe);
test/standalone/dep_triangle/build.zig
@@ -7,19 +7,20 @@ pub fn build(b: *std.Build) void {
     const optimize: std.builtin.OptimizeMode = .Debug;
 
     const shared = b.createModule(.{
-        .source_file = .{ .path = "shared.zig" },
+        .root_source_file = .{ .path = "shared.zig" },
     });
 
     const exe = b.addExecutable(.{
         .name = "test",
         .root_source_file = .{ .path = "test.zig" },
+        .target = b.host,
         .optimize = optimize,
     });
-    exe.addAnonymousModule("foo", .{
-        .source_file = .{ .path = "foo.zig" },
-        .dependencies = &.{.{ .name = "shared", .module = shared }},
+    exe.root_module.addAnonymousImport("foo", .{
+        .root_source_file = .{ .path = "foo.zig" },
+        .imports = &.{.{ .name = "shared", .module = shared }},
     });
-    exe.addModule("shared", shared);
+    exe.root_module.addImport("shared", shared);
 
     const run = b.addRunArtifact(exe);
     test_step.dependOn(&run.step);
test/standalone/embed_generated_file/build.zig
@@ -7,10 +7,10 @@ pub fn build(b: *std.Build) void {
     const bootloader = b.addExecutable(.{
         .name = "bootloader",
         .root_source_file = .{ .path = "bootloader.zig" },
-        .target = .{
+        .target = b.resolveTargetQuery(.{
             .cpu_arch = .x86,
             .os_tag = .freestanding,
-        },
+        }),
         .optimize = .ReleaseSmall,
     });
 
@@ -18,8 +18,8 @@ pub fn build(b: *std.Build) void {
         .root_source_file = .{ .path = "main.zig" },
         .optimize = .Debug,
     });
-    exe.addAnonymousModule("bootloader.elf", .{
-        .source_file = bootloader.getEmittedBin(),
+    exe.root_module.addAnonymousImport("bootloader.elf", .{
+        .root_source_file = bootloader.getEmittedBin(),
     });
 
     // TODO: actually check the output
test/standalone/empty_env/build.zig
@@ -15,6 +15,7 @@ pub fn build(b: *std.Build) void {
     const main = b.addExecutable(.{
         .name = "main",
         .root_source_file = .{ .path = "main.zig" },
+        .target = b.host,
         .optimize = optimize,
     });
 
test/standalone/extern/build.zig
@@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void {
     const obj = b.addObject(.{
         .name = "exports",
         .root_source_file = .{ .path = "exports.zig" },
-        .target = .{},
+        .target = b.host,
         .optimize = optimize,
     });
     const main = b.addTest(.{
test/standalone/global_linkage/build.zig
@@ -5,7 +5,7 @@ pub fn build(b: *std.Build) void {
     b.default_step = test_step;
 
     const optimize: std.builtin.OptimizeMode = .Debug;
-    const target: std.zig.CrossTarget = .{};
+    const target = b.host;
 
     const obj1 = b.addStaticLibrary(.{
         .name = "obj1",
test/standalone/install_raw_hex/build.zig
@@ -5,12 +5,12 @@ pub fn build(b: *std.Build) void {
     const test_step = b.step("test", "Test it");
     b.default_step = test_step;
 
-    const target = .{
+    const target = b.resolveTargetQuery(.{
         .cpu_arch = .thumb,
         .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 },
         .os_tag = .freestanding,
         .abi = .gnueabihf,
-    };
+    });
 
     const optimize: std.builtin.OptimizeMode = .Debug;
 
test/standalone/ios/build.zig
@@ -8,12 +8,12 @@ pub fn build(b: *std.Build) void {
     b.default_step = test_step;
 
     const optimize: std.builtin.OptimizeMode = .Debug;
-    const target: std.zig.CrossTarget = .{
+    const target = b.resolveTargetQuery(.{
         .cpu_arch = .aarch64,
         .os_tag = .ios,
-    };
-    const target_info = std.zig.system.NativeTargetInfo.detect(target) catch @panic("couldn't detect native target");
-    const sdk = std.zig.system.darwin.getSdk(b.allocator, target_info.target) orelse @panic("no iOS SDK found");
+    });
+    const sdk = std.zig.system.darwin.getSdk(b.allocator, target.target) orelse
+        @panic("no iOS SDK found");
     b.sysroot = sdk;
 
     const exe = b.addExecutable(.{
test/standalone/issue_11595/build.zig
@@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void {
     b.default_step = test_step;
 
     const optimize: std.builtin.OptimizeMode = .Debug;
-    const target: std.zig.CrossTarget = .{};
+    const target = b.host;
 
     if (builtin.os.tag == .windows) {
         // https://github.com/ziglang/zig/issues/12419
test/standalone/issue_12588/build.zig
@@ -5,13 +5,12 @@ pub fn build(b: *std.Build) void {
     b.default_step = test_step;
 
     const optimize: std.builtin.OptimizeMode = .Debug;
-    const target: std.zig.CrossTarget = .{};
 
     const obj = b.addObject(.{
         .name = "main",
         .root_source_file = .{ .path = "main.zig" },
         .optimize = optimize,
-        .target = target,
+        .target = b.host,
     });
     _ = obj.getEmittedLlvmIr();
     _ = obj.getEmittedLlvmBc();
test/standalone/issue_12706/build.zig
@@ -1,13 +1,12 @@
 const std = @import("std");
 const builtin = @import("builtin");
-const CrossTarget = std.zig.CrossTarget;
 
 pub fn build(b: *std.Build) void {
     const test_step = b.step("test", "Test it");
     b.default_step = test_step;
 
     const optimize: std.builtin.OptimizeMode = .Debug;
-    const target: std.zig.CrossTarget = .{};
+    const target = b.host;
 
     const exe = b.addExecutable(.{
         .name = "main",
test/standalone/issue_339/build.zig
@@ -5,7 +5,7 @@ pub fn build(b: *std.Build) void {
     b.default_step = test_step;
 
     const optimize: std.builtin.OptimizeMode = .Debug;
-    const target: std.zig.CrossTarget = .{};
+    const target = b.host;
 
     const obj = b.addObject(.{
         .name = "test",
test/standalone/issue_8550/build.zig
@@ -5,13 +5,13 @@ pub fn build(b: *std.Build) !void {
     b.default_step = test_step;
 
     const optimize: std.builtin.OptimizeMode = .Debug;
-    const target = std.zig.CrossTarget{
+    const target = b.resolveTargetQuery(.{
         .os_tag = .freestanding,
         .cpu_arch = .arm,
         .cpu_model = .{
             .explicit = &std.Target.arm.cpu.arm1176jz_s,
         },
-    };
+    });
 
     const kernel = b.addExecutable(.{
         .name = "kernel",
test/standalone/load_dynamic_library/build.zig
@@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void {
     b.default_step = test_step;
 
     const optimize: std.builtin.OptimizeMode = .Debug;
-    const target: std.zig.CrossTarget = .{};
+    const target = b.host;
 
     if (builtin.os.tag == .wasi) return;
 
test/standalone/main_pkg_path/a/test.zig
@@ -1,5 +0,0 @@
-const b = @import("../b.zig");
-
-test "main pkg path" {
-    b.foo();
-}
test/standalone/main_pkg_path/b.zig
@@ -1,1 +0,0 @@
-pub fn foo() void {}
test/standalone/main_pkg_path/build.zig
@@ -1,13 +0,0 @@
-const std = @import("std");
-
-pub fn build(b: *std.Build) void {
-    const test_step = b.step("test", "Test it");
-    b.default_step = test_step;
-
-    const test_exe = b.addTest(.{
-        .root_source_file = .{ .path = "a/test.zig" },
-        .main_pkg_path = .{ .path = "." },
-    });
-
-    test_step.dependOn(&b.addRunArtifact(test_exe).step);
-}
test/standalone/mix_c_files/build.zig
@@ -19,6 +19,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
     const exe = b.addExecutable(.{
         .name = "test",
         .root_source_file = .{ .path = "main.zig" },
+        .target = b.host,
         .optimize = optimize,
     });
     exe.addCSourceFile(.{ .file = .{ .path = "test.c" }, .flags = &[_][]const u8{"-std=c11"} });
test/standalone/mix_o_files/build.zig
@@ -5,7 +5,7 @@ pub fn build(b: *std.Build) void {
     b.default_step = test_step;
 
     const optimize: std.builtin.OptimizeMode = .Debug;
-    const target: std.zig.CrossTarget = .{};
+    const target = b.host;
 
     const obj = b.addObject(.{
         .name = "base64",
test/standalone/options/build.zig
@@ -3,7 +3,7 @@ const std = @import("std");
 pub fn build(b: *std.Build) void {
     const main = b.addTest(.{
         .root_source_file = .{ .path = "src/main.zig" },
-        .target = .{},
+        .target = b.host,
         .optimize = .Debug,
     });
 
test/standalone/pie/build.zig
@@ -5,10 +5,10 @@ pub fn build(b: *std.Build) void {
     b.default_step = test_step;
 
     const optimize: std.builtin.OptimizeMode = .Debug;
-    const target: std.zig.CrossTarget = .{
+    const target = b.resolveTargetQuery(.{
         .os_tag = .linux,
         .cpu_arch = .x86_64,
-    };
+    });
 
     const main = b.addTest(.{
         .root_source_file = .{ .path = "main.zig" },
test/standalone/pkg_import/build.zig
@@ -10,8 +10,9 @@ pub fn build(b: *std.Build) void {
         .name = "test",
         .root_source_file = .{ .path = "test.zig" },
         .optimize = optimize,
+        .target = b.host,
     });
-    exe.addAnonymousModule("my_pkg", .{ .source_file = .{ .path = "pkg.zig" } });
+    exe.root_module.addAnonymousImport("my_pkg", .{ .root_source_file = .{ .path = "pkg.zig" } });
 
     const run = b.addRunArtifact(exe);
     test_step.dependOn(&run.step);
test/standalone/self_exe_symlink/build.zig
@@ -7,11 +7,11 @@ pub fn build(b: *std.Build) void {
     b.default_step = test_step;
 
     const optimize: std.builtin.OptimizeMode = .Debug;
-    const target: std.zig.CrossTarget = .{};
+    const target = b.host;
 
     // The test requires getFdPath in order to to get the path of the
     // File returned by openSelfExe
-    if (!std.os.isGetFdPathSupportedOnTarget(target.getOs())) return;
+    if (!std.os.isGetFdPathSupportedOnTarget(target.target.os)) return;
 
     const main = b.addExecutable(.{
         .name = "main",
test/standalone/shared_library/build.zig
@@ -10,7 +10,7 @@ pub fn build(b: *std.Build) void {
     }
 
     const optimize: std.builtin.OptimizeMode = .Debug;
-    const target: std.zig.CrossTarget = .{};
+    const target = b.host;
     const lib = b.addSharedLibrary(.{
         .name = "mathtest",
         .root_source_file = .{ .path = "mathtest.zig" },
test/standalone/stack_iterator/build.zig
@@ -22,11 +22,10 @@ pub fn build(b: *std.Build) void {
             .root_source_file = .{ .path = "unwind.zig" },
             .target = target,
             .optimize = optimize,
+            .unwind_tables = target.target.isDarwin(),
+            .omit_frame_pointer = false,
         });
 
-        if (target.isDarwin()) exe.unwind_tables = true;
-        exe.omit_frame_pointer = false;
-
         const run_cmd = b.addRunArtifact(exe);
         test_step.dependOn(&run_cmd.step);
     }
@@ -46,11 +45,10 @@ pub fn build(b: *std.Build) void {
             .root_source_file = .{ .path = "unwind.zig" },
             .target = target,
             .optimize = optimize,
+            .unwind_tables = true,
+            .omit_frame_pointer = true,
         });
 
-        exe.omit_frame_pointer = true;
-        exe.unwind_tables = true;
-
         const run_cmd = b.addRunArtifact(exe);
         test_step.dependOn(&run_cmd.step);
     }
@@ -69,11 +67,12 @@ pub fn build(b: *std.Build) void {
             .name = "c_shared_lib",
             .target = target,
             .optimize = optimize,
+            .strip = false,
         });
 
-        if (target.isWindows()) c_shared_lib.defineCMacro("LIB_API", "__declspec(dllexport)");
+        if (target.target.os.tag == .windows)
+            c_shared_lib.defineCMacro("LIB_API", "__declspec(dllexport)");
 
-        c_shared_lib.strip = false;
         c_shared_lib.addCSourceFile(.{
             .file = .{ .path = "shared_lib.c" },
             .flags = &.{"-fomit-frame-pointer"},
@@ -85,10 +84,10 @@ pub fn build(b: *std.Build) void {
             .root_source_file = .{ .path = "shared_lib_unwind.zig" },
             .target = target,
             .optimize = optimize,
+            .unwind_tables = target.target.isDarwin(),
+            .omit_frame_pointer = true,
         });
 
-        if (target.isDarwin()) exe.unwind_tables = true;
-        exe.omit_frame_pointer = true;
         exe.linkLibrary(c_shared_lib);
 
         const run_cmd = b.addRunArtifact(exe);
test/standalone/static_c_lib/build.zig
@@ -9,7 +9,7 @@ pub fn build(b: *std.Build) void {
     const foo = b.addStaticLibrary(.{
         .name = "foo",
         .optimize = optimize,
-        .target = .{},
+        .target = b.host,
     });
     foo.addCSourceFile(.{ .file = .{ .path = "foo.c" }, .flags = &[_][]const u8{} });
     foo.addIncludePath(.{ .path = "." });
test/standalone/strip_empty_loop/build.zig
@@ -5,15 +5,15 @@ pub fn build(b: *std.Build) void {
     b.default_step = test_step;
 
     const optimize = std.builtin.OptimizeMode.Debug;
-    const target = std.zig.CrossTarget{};
+    const target = b.host;
 
     const main = b.addExecutable(.{
         .name = "main",
         .root_source_file = .{ .path = "main.zig" },
         .optimize = optimize,
         .target = target,
+        .strip = true,
     });
-    main.strip = true;
 
     // TODO: actually check the output
     _ = main.getEmittedBin();
test/standalone/strip_struct_init/build.zig
@@ -9,8 +9,8 @@ pub fn build(b: *std.Build) void {
     const main = b.addTest(.{
         .root_source_file = .{ .path = "main.zig" },
         .optimize = optimize,
+        .strip = true,
     });
-    main.strip = true;
 
     test_step.dependOn(&b.addRunArtifact(main).step);
 }
test/standalone/test_runner_module_imports/build.zig
@@ -6,13 +6,13 @@ pub fn build(b: *std.Build) void {
         .test_runner = "test_runner/main.zig",
     });
 
-    const module1 = b.createModule(.{ .source_file = .{ .path = "module1/main.zig" } });
+    const module1 = b.createModule(.{ .root_source_file = .{ .path = "module1/main.zig" } });
     const module2 = b.createModule(.{
-        .source_file = .{ .path = "module2/main.zig" },
-        .dependencies = &.{.{ .name = "module1", .module = module1 }},
+        .root_source_file = .{ .path = "module2/main.zig" },
+        .imports = &.{.{ .name = "module1", .module = module1 }},
     });
 
-    t.addModule("module2", module2);
+    t.root_module.addImport("module2", module2);
 
     const test_step = b.step("test", "Run unit tests");
     test_step.dependOn(&b.addRunArtifact(t).step);
test/standalone/windows_resources/build.zig
@@ -4,21 +4,25 @@ pub fn build(b: *std.Build) void {
     const test_step = b.step("test", "Test it");
     b.default_step = test_step;
 
-    const native_target: std.zig.CrossTarget = .{};
-    const cross_target = .{
+    const cross_target = b.resolveTargetQuery(.{
         .cpu_arch = .x86_64,
         .os_tag = .windows,
         .abi = .gnu,
-    };
+    });
 
-    add(b, native_target, .any, test_step);
+    add(b, b.host, .any, test_step);
     add(b, cross_target, .any, test_step);
 
-    add(b, native_target, .gnu, test_step);
+    add(b, b.host, .gnu, test_step);
     add(b, cross_target, .gnu, test_step);
 }
 
-fn add(b: *std.Build, target: std.zig.CrossTarget, rc_includes: enum { any, gnu }, test_step: *std.Build.Step) void {
+fn add(
+    b: *std.Build,
+    target: std.Build.ResolvedTarget,
+    rc_includes: enum { any, gnu },
+    test_step: *std.Build.Step,
+) void {
     const exe = b.addExecutable(.{
         .name = "zig_resource_test",
         .root_source_file = .{ .path = "main.zig" },
test/standalone/windows_spawn/build.zig
@@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void {
     b.default_step = test_step;
 
     const optimize: std.builtin.OptimizeMode = .Debug;
-    const target: std.zig.CrossTarget = .{};
+    const target = b.host;
 
     if (builtin.os.tag != .windows) return;
 
test/standalone/zerolength_check/build.zig
@@ -13,11 +13,11 @@ pub fn build(b: *std.Build) void {
 fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
     const unit_tests = b.addTest(.{
         .root_source_file = .{ .path = "src/main.zig" },
-        .target = .{
+        .target = b.resolveTargetQuery(.{
             .os_tag = .wasi,
             .cpu_arch = .wasm32,
             .cpu_features_add = std.Target.wasm.featureSet(&.{.bulk_memory}),
-        },
+        }),
         .optimize = optimize,
     });
 
test/cases.zig
@@ -9,9 +9,9 @@ pub const BuildOptions = struct {
     llvm_has_xtensa: bool,
 };
 
-pub fn addCases(cases: *Cases, build_options: BuildOptions) !void {
-    try @import("compile_errors.zig").addCases(cases);
-    try @import("cbe.zig").addCases(cases);
-    try @import("llvm_targets.zig").addCases(cases, build_options);
-    try @import("nvptx.zig").addCases(cases);
+pub fn addCases(cases: *Cases, build_options: BuildOptions, b: *std.Build) !void {
+    try @import("compile_errors.zig").addCases(cases, b);
+    try @import("cbe.zig").addCases(cases, b);
+    try @import("llvm_targets.zig").addCases(cases, build_options, b);
+    try @import("nvptx.zig").addCases(cases, b);
 }
test/cbe.zig
@@ -2,16 +2,16 @@ const std = @import("std");
 const Cases = @import("src/Cases.zig");
 const nl = if (@import("builtin").os.tag == .windows) "\r\n" else "\n";
 
-// These tests should work with all platforms, but we're using linux_x64 for
-// now for consistency. Will be expanded eventually.
-const linux_x64 = std.zig.CrossTarget{
-    .cpu_arch = .x86_64,
-    .os_tag = .linux,
-};
-
-pub fn addCases(ctx: *Cases) !void {
+pub fn addCases(ctx: *Cases, b: *std.Build) !void {
+    // These tests should work with all platforms, but we're using linux_x64 for
+    // now for consistency. Will be expanded eventually.
+    const linux_x64: std.zig.CrossTarget = .{
+        .cpu_arch = .x86_64,
+        .os_tag = .linux,
+    };
+
     {
-        var case = ctx.exeFromCompiledC("hello world with updates", .{});
+        var case = ctx.exeFromCompiledC("hello world with updates", .{}, b);
 
         // Regular old hello world
         case.addCompareOutput(
@@ -59,7 +59,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        var case = ctx.exeFromCompiledC("var args", .{});
+        var case = ctx.exeFromCompiledC("var args", .{}, b);
 
         case.addCompareOutput(
             \\extern fn printf(format: [*:0]const u8, ...) c_int;
@@ -72,7 +72,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        var case = ctx.exeFromCompiledC("errorFromInt", .{});
+        var case = ctx.exeFromCompiledC("errorFromInt", .{}, b);
 
         case.addCompareOutput(
             \\pub export fn main() c_int {
@@ -108,7 +108,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64);
+        var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64, b);
 
         // Exit with 0
         case.addCompareOutput(
@@ -202,7 +202,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        var case = ctx.exeFromCompiledC("alloc and retptr", .{});
+        var case = ctx.exeFromCompiledC("alloc and retptr", .{}, b);
 
         case.addCompareOutput(
             \\fn add(a: i32, b: i32) i32 {
@@ -220,7 +220,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        var case = ctx.exeFromCompiledC("inferred local const and var", .{});
+        var case = ctx.exeFromCompiledC("inferred local const and var", .{}, b);
 
         case.addCompareOutput(
             \\fn add(a: i32, b: i32) i32 {
@@ -236,7 +236,7 @@ pub fn addCases(ctx: *Cases) !void {
         , "");
     }
     {
-        var case = ctx.exeFromCompiledC("control flow", .{});
+        var case = ctx.exeFromCompiledC("control flow", .{}, b);
 
         // Simple while loop
         case.addCompareOutput(
@@ -423,7 +423,7 @@ pub fn addCases(ctx: *Cases) !void {
         });
     }
     //{
-    //    var case = ctx.exeFromCompiledC("optionals", .{});
+    //    var case = ctx.exeFromCompiledC("optionals", .{}, b);
 
     //    // Simple while loop
     //    case.addCompareOutput(
@@ -451,7 +451,7 @@ pub fn addCases(ctx: *Cases) !void {
     //}
 
     {
-        var case = ctx.exeFromCompiledC("errors", .{});
+        var case = ctx.exeFromCompiledC("errors", .{}, b);
         case.addCompareOutput(
             \\pub export fn main() c_int {
             \\    var e1 = error.Foo;
@@ -495,7 +495,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        var case = ctx.exeFromCompiledC("structs", .{});
+        var case = ctx.exeFromCompiledC("structs", .{}, b);
         case.addError(
             \\const Point = struct { x: i32, y: i32 };
             \\pub export fn main() c_int {
@@ -562,7 +562,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        var case = ctx.exeFromCompiledC("unions", .{});
+        var case = ctx.exeFromCompiledC("unions", .{}, b);
 
         case.addError(
             \\const U = union {
@@ -596,7 +596,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        var case = ctx.exeFromCompiledC("enums", .{});
+        var case = ctx.exeFromCompiledC("enums", .{}, b);
 
         case.addError(
             \\const E1 = packed enum { a, b, c };
@@ -838,7 +838,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        var case = ctx.exeFromCompiledC("shift right and left", .{});
+        var case = ctx.exeFromCompiledC("shift right and left", .{}, b);
         case.addCompareOutput(
             \\pub export fn main() c_int {
             \\    var i: u32 = 16;
@@ -863,7 +863,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        var case = ctx.exeFromCompiledC("inferred error sets", .{});
+        var case = ctx.exeFromCompiledC("inferred error sets", .{}, b);
 
         case.addCompareOutput(
             \\pub export fn main() c_int {
@@ -884,7 +884,7 @@ pub fn addCases(ctx: *Cases) !void {
 
     {
         // TODO: add u64 tests, ran into issues with the literal generated for std.math.maxInt(u64)
-        var case = ctx.exeFromCompiledC("add and sub wrapping operations", .{});
+        var case = ctx.exeFromCompiledC("add and sub wrapping operations", .{}, b);
         case.addCompareOutput(
             \\pub export fn main() c_int {
             \\    // Addition
@@ -933,7 +933,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        var case = ctx.exeFromCompiledC("rem", linux_x64);
+        var case = ctx.exeFromCompiledC("rem", linux_x64, b);
         case.addCompareOutput(
             \\fn assert(ok: bool) void {
             \\    if (!ok) unreachable;
test/compile_errors.zig
@@ -2,9 +2,9 @@ const std = @import("std");
 const builtin = @import("builtin");
 const Cases = @import("src/Cases.zig");
 
-pub fn addCases(ctx: *Cases) !void {
+pub fn addCases(ctx: *Cases, b: *std.Build) !void {
     {
-        const case = ctx.obj("multiline error messages", .{});
+        const case = ctx.obj("multiline error messages", b.host);
 
         case.addError(
             \\comptime {
@@ -39,7 +39,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        const case = ctx.obj("isolated carriage return in multiline string literal", .{});
+        const case = ctx.obj("isolated carriage return in multiline string literal", b.host);
 
         case.addError("const foo = \\\\\test\r\r rogue carriage return\n;", &[_][]const u8{
             ":1:19: error: expected ';' after declaration",
@@ -48,7 +48,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        const case = ctx.obj("missing semicolon at EOF", .{});
+        const case = ctx.obj("missing semicolon at EOF", b.host);
         case.addError(
             \\const foo = 1
         , &[_][]const u8{
@@ -57,7 +57,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        const case = ctx.obj("argument causes error", .{});
+        const case = ctx.obj("argument causes error", b.host);
 
         case.addError(
             \\pub export fn entry() void {
@@ -80,7 +80,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        const case = ctx.obj("astgen failure in file struct", .{});
+        const case = ctx.obj("astgen failure in file struct", b.host);
 
         case.addError(
             \\pub export fn entry() void {
@@ -95,7 +95,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        const case = ctx.obj("invalid store to comptime field", .{});
+        const case = ctx.obj("invalid store to comptime field", b.host);
 
         case.addError(
             \\const a = @import("a.zig");
@@ -119,7 +119,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        const case = ctx.obj("file in multiple modules", .{});
+        const case = ctx.obj("file in multiple modules", b.host);
         case.addDepModule("foo", "foo.zig");
 
         case.addError(
@@ -138,7 +138,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        const case = ctx.obj("wrong same named struct", .{});
+        const case = ctx.obj("wrong same named struct", b.host);
 
         case.addError(
             \\const a = @import("a.zig");
@@ -172,7 +172,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        const case = ctx.obj("non-printable invalid character", .{});
+        const case = ctx.obj("non-printable invalid character", b.host);
 
         case.addError("\xff\xfe" ++
             \\export fn foo() bool {
@@ -185,7 +185,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        const case = ctx.obj("imported generic method call with invalid param", .{});
+        const case = ctx.obj("imported generic method call with invalid param", b.host);
 
         case.addError(
             \\pub const import = @import("import.zig");
test/llvm_targets.zig
@@ -127,17 +127,21 @@ const targets = [_]std.zig.CrossTarget{
     .{ .cpu_arch = .xtensa, .os_tag = .linux, .abi = .none },
 };
 
-pub fn addCases(ctx: *Cases, build_options: @import("cases.zig").BuildOptions) !void {
+pub fn addCases(
+    ctx: *Cases,
+    build_options: @import("cases.zig").BuildOptions,
+    b: *std.Build,
+) !void {
     if (!build_options.enable_llvm) return;
-    for (targets) |target| {
-        if (target.cpu_arch) |arch| switch (arch) {
+    for (targets) |target_query| {
+        if (target_query.cpu_arch) |arch| switch (arch) {
             .m68k => if (!build_options.llvm_has_m68k) continue,
             .csky => if (!build_options.llvm_has_csky) continue,
             .arc => if (!build_options.llvm_has_arc) continue,
             .xtensa => if (!build_options.llvm_has_xtensa) continue,
             else => {},
         };
-        var case = ctx.noEmitUsingLlvmBackend("llvm_targets", target);
+        var case = ctx.noEmitUsingLlvmBackend("llvm_targets", b.resolveTargetQuery(target_query));
         case.addCompile("");
     }
 }
test/nvptx.zig
@@ -1,9 +1,14 @@
 const std = @import("std");
 const Cases = @import("src/Cases.zig");
 
-pub fn addCases(ctx: *Cases) !void {
+pub fn addCases(ctx: *Cases, b: *std.Build) !void {
+    const target = b.resolveTargetQuery(.{
+        .cpu_arch = .nvptx64,
+        .os_tag = .cuda,
+    });
+
     {
-        var case = addPtx(ctx, "simple addition and subtraction");
+        var case = addPtx(ctx, target, "simple addition and subtraction");
 
         case.addCompile(
             \\fn add(a: i32, b: i32) i32 {
@@ -20,7 +25,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        var case = addPtx(ctx, "read special registers");
+        var case = addPtx(ctx, target, "read special registers");
 
         case.addCompile(
             \\fn threadIdX() u32 {
@@ -37,7 +42,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        var case = addPtx(ctx, "address spaces");
+        var case = addPtx(ctx, target, "address spaces");
 
         case.addCompile(
             \\var x: i32 addrspace(.global) = 0;
@@ -50,7 +55,7 @@ pub fn addCases(ctx: *Cases) !void {
     }
 
     {
-        var case = addPtx(ctx, "reduce in shared mem");
+        var case = addPtx(ctx, target, "reduce in shared mem");
         case.addCompile(
             \\fn threadIdX() u32 {
             \\    return asm ("mov.u32 \t%[r], %tid.x;"
@@ -82,18 +87,10 @@ pub fn addCases(ctx: *Cases) !void {
     }
 }
 
-const nvptx_target = std.zig.CrossTarget{
-    .cpu_arch = .nvptx64,
-    .os_tag = .cuda,
-};
-
-pub fn addPtx(
-    ctx: *Cases,
-    name: []const u8,
-) *Cases.Case {
+fn addPtx(ctx: *Cases, target: std.Build.ResolvedTarget, name: []const u8) *Cases.Case {
     ctx.cases.append(.{
         .name = name,
-        .target = nvptx_target,
+        .target = target,
         .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator),
         .output_mode = .Obj,
         .deps = std.ArrayList(Cases.DepModule).init(ctx.cases.allocator),
test/standalone.zig
@@ -89,10 +89,6 @@ pub const build_cases = [_]BuildCase{
     //    .build_root = "test/standalone/issue_13970",
     //    .import = @import("standalone/issue_13970/build.zig"),
     //},
-    .{
-        .build_root = "test/standalone/main_pkg_path",
-        .import = @import("standalone/main_pkg_path/build.zig"),
-    },
     .{
         .build_root = "test/standalone/shared_library",
         .import = @import("standalone/shared_library/build.zig"),
test/tests.zig
@@ -1,7 +1,6 @@
 const std = @import("std");
 const builtin = @import("builtin");
 const assert = std.debug.assert;
-const CrossTarget = std.zig.CrossTarget;
 const mem = std.mem;
 const OptimizeMode = std.builtin.OptimizeMode;
 const Step = std.Build.Step;
@@ -22,13 +21,13 @@ pub const CompareOutputContext = @import("src/CompareOutput.zig");
 pub const StackTracesContext = @import("src/StackTrace.zig");
 
 const TestTarget = struct {
-    target: CrossTarget = .{},
+    target: std.zig.CrossTarget = .{},
     optimize_mode: std.builtin.OptimizeMode = .Debug,
     link_libc: ?bool = null,
     single_threaded: ?bool = null,
     use_llvm: ?bool = null,
     use_lld: ?bool = null,
-    force_pic: ?bool = null,
+    pic: ?bool = null,
     strip: ?bool = null,
 };
 
@@ -105,7 +104,7 @@ const test_targets = blk: {
             },
             .use_llvm = false,
             .use_lld = false,
-            .force_pic = true,
+            .pic = true,
         },
         .{
             .target = .{
@@ -146,7 +145,7 @@ const test_targets = blk: {
         //},
         // https://github.com/ziglang/zig/issues/13623
         //.{
-        //    .target = CrossTarget.parse(.{
+        //    .target = std.zig.CrossTarget.parse(.{
         //        .arch_os_abi = "arm-linux-none",
         //        .cpu_features = "generic+v8a",
         //    }) catch unreachable,
@@ -287,13 +286,13 @@ const test_targets = blk: {
         },
 
         .{
-            .target = CrossTarget.parse(.{
+            .target = std.zig.CrossTarget.parse(.{
                 .arch_os_abi = "arm-linux-none",
                 .cpu_features = "generic+v8a",
             }) catch unreachable,
         },
         .{
-            .target = CrossTarget.parse(.{
+            .target = std.zig.CrossTarget.parse(.{
                 .arch_os_abi = "arm-linux-musleabihf",
                 .cpu_features = "generic+v8a",
             }) catch unreachable,
@@ -301,7 +300,7 @@ const test_targets = blk: {
         },
         // https://github.com/ziglang/zig/issues/3287
         //.{
-        //    .target = CrossTarget.parse(.{
+        //    .target = std.zig.CrossTarget.parse(.{
         //        .arch_os_abi = "arm-linux-gnueabihf",
         //        .cpu_features = "generic+v8a",
         //    }) catch unreachable,
@@ -495,10 +494,10 @@ const test_targets = blk: {
 };
 
 const CAbiTarget = struct {
-    target: CrossTarget = .{},
+    target: std.zig.CrossTarget = .{},
     use_llvm: ?bool = null,
     use_lld: ?bool = null,
-    force_pic: ?bool = null,
+    pic: ?bool = null,
     strip: ?bool = null,
     c_defines: []const []const u8 = &.{},
 };
@@ -543,7 +542,7 @@ const c_abi_targets = [_]CAbiTarget{
         },
         .use_llvm = false,
         .use_lld = false,
-        .force_pic = true,
+        .pic = true,
         .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"},
     },
     .{
@@ -645,7 +644,7 @@ pub fn addStackTraceTests(
     const check_exe = b.addExecutable(.{
         .name = "check-stack-trace",
         .root_source_file = .{ .path = "test/src/check-stack-trace.zig" },
-        .target = .{},
+        .target = b.host,
         .optimize = .Debug,
     });
 
@@ -682,12 +681,14 @@ pub fn addStandaloneTests(
                 if (os_tag != builtin.os.tag) continue;
             }
 
+            const resolved_target = b.resolveTargetQuery(case.target);
+
             if (case.is_exe) {
                 const exe = b.addExecutable(.{
                     .name = std.fs.path.stem(case.src_path),
                     .root_source_file = .{ .path = case.src_path },
                     .optimize = optimize,
-                    .target = case.target,
+                    .target = resolved_target,
                 });
                 if (case.link_libc) exe.linkLibC();
 
@@ -701,7 +702,7 @@ pub fn addStandaloneTests(
                     .name = std.fs.path.stem(case.src_path),
                     .root_source_file = .{ .path = case.src_path },
                     .optimize = optimize,
-                    .target = case.target,
+                    .target = resolved_target,
                 });
                 if (case.link_libc) exe.linkLibC();
 
@@ -1001,7 +1002,7 @@ pub fn addTranslateCTests(b: *std.Build, test_filter: ?[]const u8) *Step {
 pub fn addRunTranslatedCTests(
     b: *std.Build,
     test_filter: ?[]const u8,
-    target: std.zig.CrossTarget,
+    target: std.Build.ResolvedTarget,
 ) *Step {
     const cases = b.allocator.create(RunTranslatedCContext) catch @panic("OOM");
     cases.* = .{
@@ -1105,7 +1106,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
         const these_tests = b.addTest(.{
             .root_source_file = .{ .path = options.root_src },
             .optimize = test_target.optimize_mode,
-            .target = test_target.target,
+            .target = b.resolveTargetQuery(test_target.target),
             .max_rss = max_rss,
             .filter = options.test_filter,
             .link_libc = test_target.link_libc,
@@ -1113,9 +1114,9 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
             .use_llvm = test_target.use_llvm,
             .use_lld = test_target.use_lld,
             .zig_lib_dir = .{ .path = "lib" },
+            .pic = test_target.pic,
+            .strip = test_target.strip,
         });
-        these_tests.force_pic = test_target.force_pic;
-        these_tests.strip = test_target.strip;
         const single_threaded_suffix = if (test_target.single_threaded == true) "-single" else "";
         const backend_suffix = if (test_target.use_llvm == true)
             "-llvm"
@@ -1126,7 +1127,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
         else
             "";
         const use_lld = if (test_target.use_lld == false) "-no-lld" else "";
-        const use_pic = if (test_target.force_pic == true) "-pic" else "";
+        const use_pic = if (test_target.pic == true) "-pic" else "";
 
         these_tests.addIncludePath(.{ .path = "test" });
 
@@ -1154,7 +1155,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
             const compile_c = b.addExecutable(.{
                 .name = qualified_name,
                 .link_libc = test_target.link_libc,
-                .target = altered_target,
+                .target = b.resolveTargetQuery(altered_target),
                 .zig_lib_dir = .{ .path = "lib" },
             });
             compile_c.addCSourceFile(.{
@@ -1229,41 +1230,39 @@ pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *S
         for (c_abi_targets) |c_abi_target| {
             if (skip_non_native and !c_abi_target.target.isNative()) continue;
 
-            if (c_abi_target.target.isWindows() and c_abi_target.target.getCpuArch() == .aarch64) {
+            const resolved_target = b.resolveTargetQuery(c_abi_target.target);
+            const target = resolved_target.target;
+
+            if (target.os.tag == .windows and target.cpu.arch == .aarch64) {
                 // https://github.com/ziglang/zig/issues/14908
                 continue;
             }
 
             const test_step = b.addTest(.{
                 .name = b.fmt("test-c-abi-{s}-{s}-{s}{s}{s}{s}", .{
-                    c_abi_target.target.zigTriple(b.allocator) catch @panic("OOM"),
-                    c_abi_target.target.getCpuModel().name,
+                    target.zigTriple(b.allocator) catch @panic("OOM"),
+                    target.cpu.model.name,
                     @tagName(optimize_mode),
                     if (c_abi_target.use_llvm == true)
                         "-llvm"
-                    else if (c_abi_target.target.ofmt == std.Target.ObjectFormat.c)
+                    else if (target.ofmt == .c)
                         "-cbe"
                     else if (c_abi_target.use_llvm == false)
                         "-selfhosted"
                     else
                         "",
                     if (c_abi_target.use_lld == false) "-no-lld" else "",
-                    if (c_abi_target.force_pic == true) "-pic" else "",
+                    if (c_abi_target.pic == true) "-pic" else "",
                 }),
                 .root_source_file = .{ .path = "test/c_abi/main.zig" },
-                .target = c_abi_target.target,
+                .target = resolved_target,
                 .optimize = optimize_mode,
                 .link_libc = true,
                 .use_llvm = c_abi_target.use_llvm,
                 .use_lld = c_abi_target.use_lld,
+                .pic = c_abi_target.pic,
+                .strip = c_abi_target.strip,
             });
-            test_step.force_pic = c_abi_target.force_pic;
-            test_step.strip = c_abi_target.strip;
-            if (c_abi_target.target.abi != null and c_abi_target.target.abi.?.isMusl()) {
-                // TODO NativeTargetInfo insists on dynamically linking musl
-                // for some reason?
-                test_step.target_info.dynamic_linker.max_byte = null;
-            }
             test_step.addCSourceFile(.{
                 .file = .{ .path = "test/c_abi/cfuncs.c" },
                 .flags = &.{"-std=c99"},
@@ -1297,8 +1296,8 @@ pub fn addCases(
     var dir = try b.build_root.handle.openDir("test/cases", .{ .iterate = true });
     defer dir.close();
 
-    cases.addFromDir(dir);
-    try @import("cases.zig").addCases(&cases, build_options);
+    cases.addFromDir(dir, b);
+    try @import("cases.zig").addCases(&cases, build_options, b);
 
     const cases_dir_path = try b.build_root.join(b.allocator, &.{ "test", "cases" });
     cases.lowerToBuildSteps(
build.zig
@@ -39,10 +39,10 @@ pub fn build(b: *std.Build) !void {
     const docgen_exe = b.addExecutable(.{
         .name = "docgen",
         .root_source_file = .{ .path = "tools/docgen.zig" },
-        .target = .{},
+        .target = b.host,
         .optimize = .Debug,
+        .single_threaded = single_threaded,
     });
-    docgen_exe.single_threaded = single_threaded;
 
     const docgen_cmd = b.addRunArtifact(docgen_exe);
     docgen_cmd.addArgs(&.{ "--zig", b.zig_exe });
@@ -89,11 +89,11 @@ pub fn build(b: *std.Build) !void {
     const check_case_exe = b.addExecutable(.{
         .name = "check-case",
         .root_source_file = .{ .path = "test/src/Cases.zig" },
+        .target = b.host,
         .optimize = optimize,
-        .main_mod_path = .{ .path = "." },
+        .single_threaded = single_threaded,
     });
     check_case_exe.stack_size = stack_size;
-    check_case_exe.single_threaded = single_threaded;
 
     const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false;
     const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
@@ -194,14 +194,18 @@ pub fn build(b: *std.Build) !void {
         break :blk 4;
     };
 
-    const exe = addCompilerStep(b, optimize, target);
-    exe.strip = strip;
+    const exe = addCompilerStep(b, .{
+        .optimize = optimize,
+        .target = target,
+        .strip = strip,
+        .sanitize_thread = sanitize_thread,
+        .single_threaded = single_threaded,
+    });
     exe.pie = pie;
-    exe.sanitize_thread = sanitize_thread;
     exe.entitlements = entitlements;
 
     exe.build_id = b.option(
-        std.Build.Step.Compile.BuildId,
+        std.zig.BuildId,
         "build-id",
         "Request creation of '.note.gnu.build-id' section",
     );
@@ -217,9 +221,7 @@ pub fn build(b: *std.Build) !void {
 
     test_step.dependOn(&exe.step);
 
-    exe.single_threaded = single_threaded;
-
-    if (target.isWindows() and target.getAbi() == .gnu) {
+    if (target.target.os.tag == .windows and target.target.abi == .gnu) {
         // LTO is currently broken on mingw, this can be removed when it's fixed.
         exe.want_lto = false;
         check_case_exe.want_lto = false;
@@ -230,7 +232,7 @@ pub fn build(b: *std.Build) !void {
     exe.use_lld = use_llvm;
 
     const exe_options = b.addOptions();
-    exe.addOptions("build_options", exe_options);
+    exe.root_module.addOptions("build_options", exe_options);
 
     exe_options.addOption(u32, "mem_leak_frames", mem_leak_frames);
     exe_options.addOption(bool, "skip_non_native", skip_non_native);
@@ -345,7 +347,7 @@ pub fn build(b: *std.Build) !void {
             try addStaticLlvmOptionsToExe(exe);
             try addStaticLlvmOptionsToExe(check_case_exe);
         }
-        if (target.isWindows()) {
+        if (target.target.os.tag == .windows) {
             inline for (.{ exe, check_case_exe }) |artifact| {
                 artifact.linkSystemLibrary("version");
                 artifact.linkSystemLibrary("uuid");
@@ -369,7 +371,7 @@ pub fn build(b: *std.Build) !void {
         );
 
         // On mingw, we need to opt into windows 7+ to get some features required by tracy.
-        const tracy_c_flags: []const []const u8 = if (target.isWindows() and target.getAbi() == .gnu)
+        const tracy_c_flags: []const []const u8 = if (target.target.os.tag == .windows and target.target.abi == .gnu)
             &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined", "-D_WIN32_WINNT=0x601" }
         else
             &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" };
@@ -377,11 +379,11 @@ pub fn build(b: *std.Build) !void {
         exe.addIncludePath(.{ .cwd_relative = tracy_path });
         exe.addCSourceFile(.{ .file = .{ .cwd_relative = client_cpp }, .flags = tracy_c_flags });
         if (!enable_llvm) {
-            exe.linkSystemLibraryName("c++");
+            exe.root_module.linkSystemLibrary("c++", .{ .use_pkg_config = .no });
         }
         exe.linkLibC();
 
-        if (target.isWindows()) {
+        if (target.target.os.tag == .windows) {
             exe.linkSystemLibrary("dbghelp");
             exe.linkSystemLibrary("ws2_32");
         }
@@ -390,7 +392,7 @@ pub fn build(b: *std.Build) !void {
     const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
 
     const test_cases_options = b.addOptions();
-    check_case_exe.addOptions("build_options", test_cases_options);
+    check_case_exe.root_module.addOptions("build_options", test_cases_options);
 
     test_cases_options.addOption(bool, "enable_tracy", false);
     test_cases_options.addOption(bool, "enable_logging", enable_logging);
@@ -540,16 +542,19 @@ pub fn build(b: *std.Build) !void {
 fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
     const semver = try std.SemanticVersion.parse(version);
 
-    var target: std.zig.CrossTarget = .{
+    var target_query: std.zig.CrossTarget = .{
         .cpu_arch = .wasm32,
         .os_tag = .wasi,
     };
-    target.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.bulk_memory));
+    target_query.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.bulk_memory));
 
-    const exe = addCompilerStep(b, .ReleaseSmall, target);
+    const exe = addCompilerStep(b, .{
+        .optimize = .ReleaseSmall,
+        .target = b.resolveTargetQuery(target_query),
+    });
 
     const exe_options = b.addOptions();
-    exe.addOptions("build_options", exe_options);
+    exe.root_module.addOptions("build_options", exe_options);
 
     exe_options.addOption(u32, "mem_leak_frames", 0);
     exe_options.addOption(bool, "have_llvm", false);
@@ -584,17 +589,24 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
     update_zig1_step.dependOn(&copy_zig_h.step);
 }
 
-fn addCompilerStep(
-    b: *std.Build,
+const AddCompilerStepOptions = struct {
     optimize: std.builtin.OptimizeMode,
-    target: std.zig.CrossTarget,
-) *std.Build.Step.Compile {
+    target: std.Build.ResolvedTarget,
+    strip: ?bool = null,
+    sanitize_thread: ?bool = null,
+    single_threaded: ?bool = null,
+};
+
+fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.Step.Compile {
     const exe = b.addExecutable(.{
         .name = "zig",
         .root_source_file = .{ .path = "src/main.zig" },
-        .target = target,
-        .optimize = optimize,
+        .target = options.target,
+        .optimize = options.optimize,
         .max_rss = 7_000_000_000,
+        .strip = options.strip,
+        .sanitize_thread = options.sanitize_thread,
+        .single_threaded = options.single_threaded,
     });
     exe.stack_size = stack_size;
 
@@ -602,15 +614,15 @@ fn addCompilerStep(
     aro_options.addOption([]const u8, "version_str", "aro-zig");
     const aro_options_module = aro_options.createModule();
     const aro_backend = b.createModule(.{
-        .source_file = .{ .path = "deps/aro/backend.zig" },
-        .dependencies = &.{.{
+        .root_source_file = .{ .path = "deps/aro/backend.zig" },
+        .imports = &.{.{
             .name = "build_options",
             .module = aro_options_module,
         }},
     });
     const aro_module = b.createModule(.{
-        .source_file = .{ .path = "deps/aro/aro.zig" },
-        .dependencies = &.{
+        .root_source_file = .{ .path = "deps/aro/aro.zig" },
+        .imports = &.{
             .{
                 .name = "build_options",
                 .module = aro_options_module,
@@ -625,7 +637,7 @@ fn addCompilerStep(
         },
     });
 
-    exe.addModule("aro", aro_module);
+    exe.root_module.addImport("aro", aro_module);
     return exe;
 }
 
@@ -649,7 +661,7 @@ fn addCmakeCfgOptionsToExe(
     exe: *std.Build.Step.Compile,
     use_zig_libcxx: bool,
 ) !void {
-    if (exe.target.isDarwin()) {
+    if (exe.rootModuleTarget().isDarwin()) {
         // useful for package maintainers
         exe.headerpad_max_install_names = true;
     }
@@ -677,8 +689,8 @@ fn addCmakeCfgOptionsToExe(
         // against system-provided LLVM, Clang, LLD.
         const need_cpp_includes = true;
         const static = cfg.llvm_linkage == .static;
-        const lib_suffix = if (static) exe.target.staticLibSuffix()[1..] else exe.target.dynamicLibSuffix()[1..];
-        switch (exe.target.getOsTag()) {
+        const lib_suffix = if (static) exe.rootModuleTarget().staticLibSuffix()[1..] else exe.rootModuleTarget().dynamicLibSuffix()[1..];
+        switch (exe.rootModuleTarget().os.tag) {
             .linux => {
                 // First we try to link against the detected libcxx name. If that doesn't work, we fall
                 // back to -lc++ and cross our fingers.
@@ -694,7 +706,7 @@ fn addCmakeCfgOptionsToExe(
                 exe.linkLibCpp();
             },
             .windows => {
-                if (exe.target.getAbi() != .msvc) exe.linkLibCpp();
+                if (exe.rootModuleTarget().abi != .msvc) exe.linkLibCpp();
             },
             .freebsd => {
                 if (static) {
@@ -756,12 +768,12 @@ fn addStaticLlvmOptionsToExe(exe: *std.Build.Step.Compile) !void {
     exe.linkSystemLibrary("z");
     exe.linkSystemLibrary("zstd");
 
-    if (exe.target.getOs().tag != .windows or exe.target.getAbi() != .msvc) {
+    if (exe.rootModuleTarget().os.tag != .windows or exe.rootModuleTarget().abi != .msvc) {
         // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries.
         exe.linkSystemLibrary("c++");
     }
 
-    if (exe.target.getOs().tag == .windows) {
+    if (exe.rootModuleTarget().os.tag == .windows) {
         exe.linkSystemLibrary("version");
         exe.linkSystemLibrary("uuid");
         exe.linkSystemLibrary("ole32");
@@ -810,7 +822,9 @@ fn addCMakeLibraryList(exe: *std.Build.Step.Compile, list: []const u8) void {
     while (it.next()) |lib| {
         if (mem.startsWith(u8, lib, "-l")) {
             exe.linkSystemLibrary(lib["-l".len..]);
-        } else if (exe.target.isWindows() and mem.endsWith(u8, lib, ".lib") and !fs.path.isAbsolute(lib)) {
+        } else if (exe.rootModuleTarget().os.tag == .windows and
+            mem.endsWith(u8, lib, ".lib") and !fs.path.isAbsolute(lib))
+        {
             exe.linkSystemLibrary(lib[0 .. lib.len - ".lib".len]);
         } else {
             exe.addObjectFile(.{ .cwd_relative = lib });