Commit a4380a30f5

Andrew Kelley <andrew@ziglang.org>
2024-02-28 01:12:53
move `zig libc` command to be lazily built
part of #19063 This is a prerequisite for doing the same for Resinator.
1 parent 6e07888
lib/compiler/libc.zig
@@ -0,0 +1,137 @@
+const std = @import("std");
+const mem = std.mem;
+const io = std.io;
+const LibCInstallation = std.zig.LibCInstallation;
+
+const usage_libc =
+    \\Usage: zig libc
+    \\
+    \\    Detect the native libc installation and print the resulting
+    \\    paths to stdout. You can save this into a file and then edit
+    \\    the paths to create a cross compilation libc kit. Then you
+    \\    can pass `--libc [file]` for Zig to use it.
+    \\
+    \\Usage: zig libc [paths_file]
+    \\
+    \\    Parse a libc installation text file and validate it.
+    \\
+    \\Options:
+    \\  -h, --help             Print this help and exit
+    \\  -target [name]         <arch><sub>-<os>-<abi> see the targets command
+    \\  -includes              Print the libc include directories for the target
+    \\
+;
+
+pub fn main() !void {
+    var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
+    defer arena_instance.deinit();
+    const arena = arena_instance.allocator();
+    const gpa = arena;
+
+    const args = try std.process.argsAlloc(arena);
+    const zig_lib_directory = args[1];
+
+    var input_file: ?[]const u8 = null;
+    var target_arch_os_abi: []const u8 = "native";
+    var print_includes: bool = false;
+    {
+        var i: usize = 2;
+        while (i < args.len) : (i += 1) {
+            const arg = args[i];
+            if (mem.startsWith(u8, arg, "-")) {
+                if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
+                    const stdout = std.io.getStdOut().writer();
+                    try stdout.writeAll(usage_libc);
+                    return std.process.cleanExit();
+                } else if (mem.eql(u8, arg, "-target")) {
+                    if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
+                    i += 1;
+                    target_arch_os_abi = args[i];
+                } else if (mem.eql(u8, arg, "-includes")) {
+                    print_includes = true;
+                } else {
+                    fatal("unrecognized parameter: '{s}'", .{arg});
+                }
+            } else if (input_file != null) {
+                fatal("unexpected extra parameter: '{s}'", .{arg});
+            } else {
+                input_file = arg;
+            }
+        }
+    }
+
+    const target_query = std.zig.parseTargetQueryOrReportFatalError(gpa, .{
+        .arch_os_abi = target_arch_os_abi,
+    });
+    const target = std.zig.resolveTargetQueryOrFatal(target_query);
+
+    if (print_includes) {
+        const libc_installation: ?*LibCInstallation = libc: {
+            if (input_file) |libc_file| {
+                const libc = try arena.create(LibCInstallation);
+                libc.* = LibCInstallation.parse(arena, libc_file, target) catch |err| {
+                    fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) });
+                };
+                break :libc libc;
+            } else {
+                break :libc null;
+            }
+        };
+
+        const is_native_abi = target_query.isNativeAbi();
+
+        const libc_dirs = std.zig.LibCDirs.detect(
+            arena,
+            zig_lib_directory,
+            target,
+            is_native_abi,
+            true,
+            libc_installation,
+        ) catch |err| {
+            const zig_target = try target.zigTriple(arena);
+            fatal("unable to detect libc for target {s}: {s}", .{ zig_target, @errorName(err) });
+        };
+
+        if (libc_dirs.libc_include_dir_list.len == 0) {
+            const zig_target = try target.zigTriple(arena);
+            fatal("no include dirs detected for target {s}", .{zig_target});
+        }
+
+        var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
+        var writer = bw.writer();
+        for (libc_dirs.libc_include_dir_list) |include_dir| {
+            try writer.writeAll(include_dir);
+            try writer.writeByte('\n');
+        }
+        try bw.flush();
+        return std.process.cleanExit();
+    }
+
+    if (input_file) |libc_file| {
+        var libc = LibCInstallation.parse(gpa, libc_file, target) catch |err| {
+            fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) });
+        };
+        defer libc.deinit(gpa);
+    } else {
+        if (!target_query.isNative()) {
+            fatal("unable to detect libc for non-native target", .{});
+        }
+        var libc = LibCInstallation.findNative(.{
+            .allocator = gpa,
+            .verbose = true,
+            .target = target,
+        }) catch |err| {
+            fatal("unable to detect native libc: {s}", .{@errorName(err)});
+        };
+        defer libc.deinit(gpa);
+
+        var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
+        try libc.render(bw.writer());
+        try bw.flush();
+    }
+}
+
+fn fatal(comptime format: []const u8, args: anytype) noreturn {
+    std.log.err(format, args);
+    std.process.exit(1);
+}
lib/std/zig/LibCDirs.zig
@@ -0,0 +1,281 @@
+libc_include_dir_list: []const []const u8,
+libc_installation: ?*const LibCInstallation,
+libc_framework_dir_list: []const []const u8,
+sysroot: ?[]const u8,
+darwin_sdk_layout: ?DarwinSdkLayout,
+
+/// The filesystem layout of darwin SDK elements.
+pub const DarwinSdkLayout = enum {
+    /// macOS SDK layout: TOP { /usr/include, /usr/lib, /System/Library/Frameworks }.
+    sdk,
+    /// Shipped libc layout: TOP { /lib/libc/include,  /lib/libc/darwin, <NONE> }.
+    vendored,
+};
+
+pub fn detect(
+    arena: Allocator,
+    zig_lib_dir: []const u8,
+    target: std.Target,
+    is_native_abi: bool,
+    link_libc: bool,
+    libc_installation: ?*const LibCInstallation,
+) !LibCDirs {
+    if (!link_libc) {
+        return .{
+            .libc_include_dir_list = &[0][]u8{},
+            .libc_installation = null,
+            .libc_framework_dir_list = &.{},
+            .sysroot = null,
+            .darwin_sdk_layout = null,
+        };
+    }
+
+    if (libc_installation) |lci| {
+        return detectFromInstallation(arena, target, lci);
+    }
+
+    // If linking system libraries and targeting the native abi, default to
+    // using the system libc installation.
+    if (is_native_abi and !target.isMinGW()) {
+        const libc = try arena.create(LibCInstallation);
+        libc.* = LibCInstallation.findNative(.{ .allocator = arena, .target = target }) catch |err| switch (err) {
+            error.CCompilerExitCode,
+            error.CCompilerCrashed,
+            error.CCompilerCannotFindHeaders,
+            error.UnableToSpawnCCompiler,
+            error.DarwinSdkNotFound,
+            => |e| {
+                // We tried to integrate with the native system C compiler,
+                // however, it is not installed. So we must rely on our bundled
+                // libc files.
+                if (std.zig.target.canBuildLibC(target)) {
+                    return detectFromBuilding(arena, zig_lib_dir, target);
+                }
+                return e;
+            },
+            else => |e| return e,
+        };
+        return detectFromInstallation(arena, target, libc);
+    }
+
+    // If not linking system libraries, build and provide our own libc by
+    // default if possible.
+    if (std.zig.target.canBuildLibC(target)) {
+        return detectFromBuilding(arena, zig_lib_dir, target);
+    }
+
+    // If zig can't build the libc for the target and we are targeting the
+    // native abi, fall back to using the system libc installation.
+    // On windows, instead of the native (mingw) abi, we want to check
+    // for the MSVC abi as a fallback.
+    const use_system_abi = if (builtin.os.tag == .windows)
+        target.abi == .msvc
+    else
+        is_native_abi;
+
+    if (use_system_abi) {
+        const libc = try arena.create(LibCInstallation);
+        libc.* = try LibCInstallation.findNative(.{ .allocator = arena, .verbose = true, .target = target });
+        return detectFromInstallation(arena, target, libc);
+    }
+
+    return .{
+        .libc_include_dir_list = &[0][]u8{},
+        .libc_installation = null,
+        .libc_framework_dir_list = &.{},
+        .sysroot = null,
+        .darwin_sdk_layout = null,
+    };
+}
+
+fn detectFromInstallation(arena: Allocator, target: std.Target, lci: *const LibCInstallation) !LibCDirs {
+    var list = try std.ArrayList([]const u8).initCapacity(arena, 5);
+    var framework_list = std.ArrayList([]const u8).init(arena);
+
+    list.appendAssumeCapacity(lci.include_dir.?);
+
+    const is_redundant = std.mem.eql(u8, lci.sys_include_dir.?, lci.include_dir.?);
+    if (!is_redundant) list.appendAssumeCapacity(lci.sys_include_dir.?);
+
+    if (target.os.tag == .windows) {
+        if (std.fs.path.dirname(lci.sys_include_dir.?)) |sys_include_dir_parent| {
+            // This include path will only exist when the optional "Desktop development with C++"
+            // is installed. It contains headers, .rc files, and resources. It is especially
+            // necessary when working with Windows resources.
+            const atlmfc_dir = try std.fs.path.join(arena, &[_][]const u8{ sys_include_dir_parent, "atlmfc", "include" });
+            list.appendAssumeCapacity(atlmfc_dir);
+        }
+        if (std.fs.path.dirname(lci.include_dir.?)) |include_dir_parent| {
+            const um_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "um" });
+            list.appendAssumeCapacity(um_dir);
+
+            const shared_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "shared" });
+            list.appendAssumeCapacity(shared_dir);
+        }
+    }
+    if (target.os.tag == .haiku) {
+        const include_dir_path = lci.include_dir orelse return error.LibCInstallationNotAvailable;
+        const os_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os" });
+        list.appendAssumeCapacity(os_dir);
+        // Errors.h
+        const os_support_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os/support" });
+        list.appendAssumeCapacity(os_support_dir);
+
+        const config_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "config" });
+        list.appendAssumeCapacity(config_dir);
+    }
+
+    var sysroot: ?[]const u8 = null;
+
+    if (target.isDarwin()) d: {
+        const down1 = std.fs.path.dirname(lci.sys_include_dir.?) orelse break :d;
+        const down2 = std.fs.path.dirname(down1) orelse break :d;
+        try framework_list.append(try std.fs.path.join(arena, &.{ down2, "System", "Library", "Frameworks" }));
+        sysroot = down2;
+    }
+
+    return .{
+        .libc_include_dir_list = list.items,
+        .libc_installation = lci,
+        .libc_framework_dir_list = framework_list.items,
+        .sysroot = sysroot,
+        .darwin_sdk_layout = if (sysroot == null) null else .sdk,
+    };
+}
+
+pub fn detectFromBuilding(
+    arena: Allocator,
+    zig_lib_dir: []const u8,
+    target: std.Target,
+) !LibCDirs {
+    const s = std.fs.path.sep_str;
+
+    if (target.isDarwin()) {
+        const list = try arena.alloc([]const u8, 1);
+        list[0] = try std.fmt.allocPrint(
+            arena,
+            "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-macos-any",
+            .{zig_lib_dir},
+        );
+        return .{
+            .libc_include_dir_list = list,
+            .libc_installation = null,
+            .libc_framework_dir_list = &.{},
+            .sysroot = null,
+            .darwin_sdk_layout = .vendored,
+        };
+    }
+
+    const generic_name = libCGenericName(target);
+    // Some architectures are handled by the same set of headers.
+    const arch_name = if (target.abi.isMusl())
+        std.zig.target.muslArchNameHeaders(target.cpu.arch)
+    else if (target.cpu.arch.isThumb())
+        // ARM headers are valid for Thumb too.
+        switch (target.cpu.arch) {
+            .thumb => "arm",
+            .thumbeb => "armeb",
+            else => unreachable,
+        }
+    else
+        @tagName(target.cpu.arch);
+    const os_name = @tagName(target.os.tag);
+    // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name.
+    const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi);
+    const arch_include_dir = try std.fmt.allocPrint(
+        arena,
+        "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}",
+        .{ zig_lib_dir, arch_name, os_name, abi_name },
+    );
+    const generic_include_dir = try std.fmt.allocPrint(
+        arena,
+        "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}",
+        .{ zig_lib_dir, generic_name },
+    );
+    const generic_arch_name = target.osArchName();
+    const arch_os_include_dir = try std.fmt.allocPrint(
+        arena,
+        "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any",
+        .{ zig_lib_dir, generic_arch_name, os_name },
+    );
+    const generic_os_include_dir = try std.fmt.allocPrint(
+        arena,
+        "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any",
+        .{ zig_lib_dir, os_name },
+    );
+
+    const list = try arena.alloc([]const u8, 4);
+    list[0] = arch_include_dir;
+    list[1] = generic_include_dir;
+    list[2] = arch_os_include_dir;
+    list[3] = generic_os_include_dir;
+
+    return .{
+        .libc_include_dir_list = list,
+        .libc_installation = null,
+        .libc_framework_dir_list = &.{},
+        .sysroot = null,
+        .darwin_sdk_layout = .vendored,
+    };
+}
+
+fn libCGenericName(target: std.Target) [:0]const u8 {
+    switch (target.os.tag) {
+        .windows => return "mingw",
+        .macos, .ios, .tvos, .watchos => return "darwin",
+        else => {},
+    }
+    switch (target.abi) {
+        .gnu,
+        .gnuabin32,
+        .gnuabi64,
+        .gnueabi,
+        .gnueabihf,
+        .gnuf32,
+        .gnuf64,
+        .gnusf,
+        .gnux32,
+        .gnuilp32,
+        => return "glibc",
+        .musl,
+        .musleabi,
+        .musleabihf,
+        .muslx32,
+        .none,
+        => return "musl",
+        .code16,
+        .eabi,
+        .eabihf,
+        .android,
+        .msvc,
+        .itanium,
+        .cygnus,
+        .coreclr,
+        .simulator,
+        .macabi,
+        => unreachable,
+
+        .pixel,
+        .vertex,
+        .geometry,
+        .hull,
+        .domain,
+        .compute,
+        .library,
+        .raygeneration,
+        .intersection,
+        .anyhit,
+        .closesthit,
+        .miss,
+        .callable,
+        .mesh,
+        .amplification,
+        => unreachable,
+    }
+}
+
+const LibCDirs = @This();
+const builtin = @import("builtin");
+const std = @import("../std.zig");
+const LibCInstallation = std.zig.LibCInstallation;
+const Allocator = std.mem.Allocator;
lib/std/zig/LibCInstallation.zig
@@ -0,0 +1,709 @@
+//! See the render function implementation for documentation of the fields.
+
+include_dir: ?[]const u8 = null,
+sys_include_dir: ?[]const u8 = null,
+crt_dir: ?[]const u8 = null,
+msvc_lib_dir: ?[]const u8 = null,
+kernel32_lib_dir: ?[]const u8 = null,
+gcc_dir: ?[]const u8 = null,
+
+pub const FindError = error{
+    OutOfMemory,
+    FileSystem,
+    UnableToSpawnCCompiler,
+    CCompilerExitCode,
+    CCompilerCrashed,
+    CCompilerCannotFindHeaders,
+    LibCRuntimeNotFound,
+    LibCStdLibHeaderNotFound,
+    LibCKernel32LibNotFound,
+    UnsupportedArchitecture,
+    WindowsSdkNotFound,
+    DarwinSdkNotFound,
+    ZigIsTheCCompiler,
+};
+
+pub fn parse(
+    allocator: Allocator,
+    libc_file: []const u8,
+    target: std.Target,
+) !LibCInstallation {
+    var self: LibCInstallation = .{};
+
+    const fields = std.meta.fields(LibCInstallation);
+    const FoundKey = struct {
+        found: bool,
+        allocated: ?[:0]u8,
+    };
+    var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** fields.len;
+    errdefer {
+        self = .{};
+        for (found_keys) |found_key| {
+            if (found_key.allocated) |s| allocator.free(s);
+        }
+    }
+
+    const contents = try std.fs.cwd().readFileAlloc(allocator, libc_file, std.math.maxInt(usize));
+    defer allocator.free(contents);
+
+    var it = std.mem.tokenizeScalar(u8, contents, '\n');
+    while (it.next()) |line| {
+        if (line.len == 0 or line[0] == '#') continue;
+        var line_it = std.mem.splitScalar(u8, line, '=');
+        const name = line_it.first();
+        const value = line_it.rest();
+        inline for (fields, 0..) |field, i| {
+            if (std.mem.eql(u8, name, field.name)) {
+                found_keys[i].found = true;
+                if (value.len == 0) {
+                    @field(self, field.name) = null;
+                } else {
+                    found_keys[i].allocated = try allocator.dupeZ(u8, value);
+                    @field(self, field.name) = found_keys[i].allocated;
+                }
+                break;
+            }
+        }
+    }
+    inline for (fields, 0..) |field, i| {
+        if (!found_keys[i].found) {
+            log.err("missing field: {s}\n", .{field.name});
+            return error.ParseError;
+        }
+    }
+    if (self.include_dir == null) {
+        log.err("include_dir may not be empty\n", .{});
+        return error.ParseError;
+    }
+    if (self.sys_include_dir == null) {
+        log.err("sys_include_dir may not be empty\n", .{});
+        return error.ParseError;
+    }
+
+    const os_tag = target.os.tag;
+    if (self.crt_dir == null and !target.isDarwin()) {
+        log.err("crt_dir may not be empty for {s}\n", .{@tagName(os_tag)});
+        return error.ParseError;
+    }
+
+    if (self.msvc_lib_dir == null and os_tag == .windows and target.abi == .msvc) {
+        log.err("msvc_lib_dir may not be empty for {s}-{s}\n", .{
+            @tagName(os_tag),
+            @tagName(target.abi),
+        });
+        return error.ParseError;
+    }
+    if (self.kernel32_lib_dir == null and os_tag == .windows and target.abi == .msvc) {
+        log.err("kernel32_lib_dir may not be empty for {s}-{s}\n", .{
+            @tagName(os_tag),
+            @tagName(target.abi),
+        });
+        return error.ParseError;
+    }
+
+    if (self.gcc_dir == null and os_tag == .haiku) {
+        log.err("gcc_dir may not be empty for {s}\n", .{@tagName(os_tag)});
+        return error.ParseError;
+    }
+
+    return self;
+}
+
+pub fn render(self: LibCInstallation, out: anytype) !void {
+    @setEvalBranchQuota(4000);
+    const include_dir = self.include_dir orelse "";
+    const sys_include_dir = self.sys_include_dir orelse "";
+    const crt_dir = self.crt_dir orelse "";
+    const msvc_lib_dir = self.msvc_lib_dir orelse "";
+    const kernel32_lib_dir = self.kernel32_lib_dir orelse "";
+    const gcc_dir = self.gcc_dir orelse "";
+
+    try out.print(
+        \\# The directory that contains `stdlib.h`.
+        \\# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null`
+        \\include_dir={s}
+        \\
+        \\# The system-specific include directory. May be the same as `include_dir`.
+        \\# On Windows it's the directory that includes `vcruntime.h`.
+        \\# On POSIX it's the directory that includes `sys/errno.h`.
+        \\sys_include_dir={s}
+        \\
+        \\# The directory that contains `crt1.o` or `crt2.o`.
+        \\# On POSIX, can be found with `cc -print-file-name=crt1.o`.
+        \\# Not needed when targeting MacOS.
+        \\crt_dir={s}
+        \\
+        \\# The directory that contains `vcruntime.lib`.
+        \\# Only needed when targeting MSVC on Windows.
+        \\msvc_lib_dir={s}
+        \\
+        \\# The directory that contains `kernel32.lib`.
+        \\# Only needed when targeting MSVC on Windows.
+        \\kernel32_lib_dir={s}
+        \\
+        \\# The directory that contains `crtbeginS.o` and `crtendS.o`
+        \\# Only needed when targeting Haiku.
+        \\gcc_dir={s}
+        \\
+    , .{
+        include_dir,
+        sys_include_dir,
+        crt_dir,
+        msvc_lib_dir,
+        kernel32_lib_dir,
+        gcc_dir,
+    });
+}
+
+pub const FindNativeOptions = struct {
+    allocator: Allocator,
+    target: std.Target,
+
+    /// If enabled, will print human-friendly errors to stderr.
+    verbose: bool = false,
+};
+
+/// Finds the default, native libc.
+pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation {
+    var self: LibCInstallation = .{};
+
+    if (is_darwin) {
+        if (!std.zig.system.darwin.isSdkInstalled(args.allocator))
+            return error.DarwinSdkNotFound;
+        const sdk = std.zig.system.darwin.getSdk(args.allocator, args.target) orelse
+            return error.DarwinSdkNotFound;
+        defer args.allocator.free(sdk);
+
+        self.include_dir = try fs.path.join(args.allocator, &.{
+            sdk, "usr/include",
+        });
+        self.sys_include_dir = try fs.path.join(args.allocator, &.{
+            sdk, "usr/include",
+        });
+        return self;
+    } else if (is_windows) {
+        var sdk = std.zig.WindowsSdk.find(args.allocator) catch |err| switch (err) {
+            error.NotFound => return error.WindowsSdkNotFound,
+            error.PathTooLong => return error.WindowsSdkNotFound,
+            error.OutOfMemory => return error.OutOfMemory,
+        };
+        defer sdk.free(args.allocator);
+
+        try self.findNativeMsvcIncludeDir(args, &sdk);
+        try self.findNativeMsvcLibDir(args, &sdk);
+        try self.findNativeKernel32LibDir(args, &sdk);
+        try self.findNativeIncludeDirWindows(args, &sdk);
+        try self.findNativeCrtDirWindows(args, &sdk);
+    } else if (is_haiku) {
+        try self.findNativeIncludeDirPosix(args);
+        try self.findNativeCrtBeginDirHaiku(args);
+        self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib");
+    } else if (builtin.target.os.tag.isSolarish()) {
+        // There is only one libc, and its headers/libraries are always in the same spot.
+        self.include_dir = try args.allocator.dupeZ(u8, "/usr/include");
+        self.sys_include_dir = try args.allocator.dupeZ(u8, "/usr/include");
+        self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib/64");
+    } else if (std.process.can_spawn) {
+        try self.findNativeIncludeDirPosix(args);
+        switch (builtin.target.os.tag) {
+            .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib"),
+            .linux => try self.findNativeCrtDirPosix(args),
+            else => {},
+        }
+    } else {
+        return error.LibCRuntimeNotFound;
+    }
+    return self;
+}
+
+/// Must be the same allocator passed to `parse` or `findNative`.
+pub fn deinit(self: *LibCInstallation, allocator: Allocator) void {
+    const fields = std.meta.fields(LibCInstallation);
+    inline for (fields) |field| {
+        if (@field(self, field.name)) |payload| {
+            allocator.free(payload);
+        }
+    }
+    self.* = undefined;
+}
+
+fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
+    const allocator = args.allocator;
+
+    // Detect infinite loops.
+    var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
+        error.Unexpected => unreachable, // WASI-only
+        else => |e| return e,
+    };
+    defer env_map.deinit();
+    const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: {
+        if (std.mem.eql(u8, phase, "1")) {
+            try env_map.put(inf_loop_env_key, "2");
+            break :blk true;
+        } else {
+            return error.ZigIsTheCCompiler;
+        }
+    } else blk: {
+        try env_map.put(inf_loop_env_key, "1");
+        break :blk false;
+    };
+
+    const dev_null = if (is_windows) "nul" else "/dev/null";
+
+    var argv = std.ArrayList([]const u8).init(allocator);
+    defer argv.deinit();
+
+    try appendCcExe(&argv, skip_cc_env_var);
+    try argv.appendSlice(&.{
+        "-E",
+        "-Wp,-v",
+        "-xc",
+        dev_null,
+    });
+
+    const run_res = std.ChildProcess.run(.{
+        .allocator = allocator,
+        .argv = argv.items,
+        .max_output_bytes = 1024 * 1024,
+        .env_map = &env_map,
+        // Some C compilers, such as Clang, are known to rely on argv[0] to find the path
+        // to their own executable, without even bothering to resolve PATH. This results in the message:
+        // error: unable to execute command: Executable "" doesn't exist!
+        // So we use the expandArg0 variant of ChildProcess to give them a helping hand.
+        .expand_arg0 = .expand,
+    }) catch |err| switch (err) {
+        error.OutOfMemory => return error.OutOfMemory,
+        else => {
+            printVerboseInvocation(argv.items, null, args.verbose, null);
+            return error.UnableToSpawnCCompiler;
+        },
+    };
+    defer {
+        allocator.free(run_res.stdout);
+        allocator.free(run_res.stderr);
+    }
+    switch (run_res.term) {
+        .Exited => |code| if (code != 0) {
+            printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr);
+            return error.CCompilerExitCode;
+        },
+        else => {
+            printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr);
+            return error.CCompilerCrashed;
+        },
+    }
+
+    var it = std.mem.tokenizeAny(u8, run_res.stderr, "\n\r");
+    var search_paths = std.ArrayList([]const u8).init(allocator);
+    defer search_paths.deinit();
+    while (it.next()) |line| {
+        if (line.len != 0 and line[0] == ' ') {
+            try search_paths.append(line);
+        }
+    }
+    if (search_paths.items.len == 0) {
+        return error.CCompilerCannotFindHeaders;
+    }
+
+    const include_dir_example_file = if (is_haiku) "posix/stdlib.h" else "stdlib.h";
+    const sys_include_dir_example_file = if (is_windows)
+        "sys\\types.h"
+    else if (is_haiku)
+        "errno.h"
+    else
+        "sys/errno.h";
+
+    var path_i: usize = 0;
+    while (path_i < search_paths.items.len) : (path_i += 1) {
+        // search in reverse order
+        const search_path_untrimmed = search_paths.items[search_paths.items.len - path_i - 1];
+        const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
+        var search_dir = fs.cwd().openDir(search_path, .{}) catch |err| switch (err) {
+            error.FileNotFound,
+            error.NotDir,
+            error.NoDevice,
+            => continue,
+
+            else => return error.FileSystem,
+        };
+        defer search_dir.close();
+
+        if (self.include_dir == null) {
+            if (search_dir.accessZ(include_dir_example_file, .{})) |_| {
+                self.include_dir = try allocator.dupeZ(u8, search_path);
+            } else |err| switch (err) {
+                error.FileNotFound => {},
+                else => return error.FileSystem,
+            }
+        }
+
+        if (self.sys_include_dir == null) {
+            if (search_dir.accessZ(sys_include_dir_example_file, .{})) |_| {
+                self.sys_include_dir = try allocator.dupeZ(u8, search_path);
+            } else |err| switch (err) {
+                error.FileNotFound => {},
+                else => return error.FileSystem,
+            }
+        }
+
+        if (self.include_dir != null and self.sys_include_dir != null) {
+            // Success.
+            return;
+        }
+    }
+
+    return error.LibCStdLibHeaderNotFound;
+}
+
+fn findNativeIncludeDirWindows(
+    self: *LibCInstallation,
+    args: FindNativeOptions,
+    sdk: *std.zig.WindowsSdk,
+) FindError!void {
+    const allocator = args.allocator;
+
+    var search_buf: [2]Search = undefined;
+    const searches = fillSearch(&search_buf, sdk);
+
+    var result_buf = std.ArrayList(u8).init(allocator);
+    defer result_buf.deinit();
+
+    for (searches) |search| {
+        result_buf.shrinkAndFree(0);
+        try result_buf.writer().print("{s}\\Include\\{s}\\ucrt", .{ search.path, search.version });
+
+        var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) {
+            error.FileNotFound,
+            error.NotDir,
+            error.NoDevice,
+            => continue,
+
+            else => return error.FileSystem,
+        };
+        defer dir.close();
+
+        dir.accessZ("stdlib.h", .{}) catch |err| switch (err) {
+            error.FileNotFound => continue,
+            else => return error.FileSystem,
+        };
+
+        self.include_dir = try result_buf.toOwnedSlice();
+        return;
+    }
+
+    return error.LibCStdLibHeaderNotFound;
+}
+
+fn findNativeCrtDirWindows(
+    self: *LibCInstallation,
+    args: FindNativeOptions,
+    sdk: *std.zig.WindowsSdk,
+) FindError!void {
+    const allocator = args.allocator;
+
+    var search_buf: [2]Search = undefined;
+    const searches = fillSearch(&search_buf, sdk);
+
+    var result_buf = std.ArrayList(u8).init(allocator);
+    defer result_buf.deinit();
+
+    const arch_sub_dir = switch (builtin.target.cpu.arch) {
+        .x86 => "x86",
+        .x86_64 => "x64",
+        .arm, .armeb => "arm",
+        .aarch64 => "arm64",
+        else => return error.UnsupportedArchitecture,
+    };
+
+    for (searches) |search| {
+        result_buf.shrinkAndFree(0);
+        try result_buf.writer().print("{s}\\Lib\\{s}\\ucrt\\{s}", .{ search.path, search.version, arch_sub_dir });
+
+        var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) {
+            error.FileNotFound,
+            error.NotDir,
+            error.NoDevice,
+            => continue,
+
+            else => return error.FileSystem,
+        };
+        defer dir.close();
+
+        dir.accessZ("ucrt.lib", .{}) catch |err| switch (err) {
+            error.FileNotFound => continue,
+            else => return error.FileSystem,
+        };
+
+        self.crt_dir = try result_buf.toOwnedSlice();
+        return;
+    }
+    return error.LibCRuntimeNotFound;
+}
+
+fn findNativeCrtDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
+    self.crt_dir = try ccPrintFileName(.{
+        .allocator = args.allocator,
+        .search_basename = "crt1.o",
+        .want_dirname = .only_dir,
+        .verbose = args.verbose,
+    });
+}
+
+fn findNativeCrtBeginDirHaiku(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
+    self.gcc_dir = try ccPrintFileName(.{
+        .allocator = args.allocator,
+        .search_basename = "crtbeginS.o",
+        .want_dirname = .only_dir,
+        .verbose = args.verbose,
+    });
+}
+
+fn findNativeKernel32LibDir(
+    self: *LibCInstallation,
+    args: FindNativeOptions,
+    sdk: *std.zig.WindowsSdk,
+) FindError!void {
+    const allocator = args.allocator;
+
+    var search_buf: [2]Search = undefined;
+    const searches = fillSearch(&search_buf, sdk);
+
+    var result_buf = std.ArrayList(u8).init(allocator);
+    defer result_buf.deinit();
+
+    const arch_sub_dir = switch (builtin.target.cpu.arch) {
+        .x86 => "x86",
+        .x86_64 => "x64",
+        .arm, .armeb => "arm",
+        .aarch64 => "arm64",
+        else => return error.UnsupportedArchitecture,
+    };
+
+    for (searches) |search| {
+        result_buf.shrinkAndFree(0);
+        const stream = result_buf.writer();
+        try stream.print("{s}\\Lib\\{s}\\um\\{s}", .{ search.path, search.version, arch_sub_dir });
+
+        var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) {
+            error.FileNotFound,
+            error.NotDir,
+            error.NoDevice,
+            => continue,
+
+            else => return error.FileSystem,
+        };
+        defer dir.close();
+
+        dir.accessZ("kernel32.lib", .{}) catch |err| switch (err) {
+            error.FileNotFound => continue,
+            else => return error.FileSystem,
+        };
+
+        self.kernel32_lib_dir = try result_buf.toOwnedSlice();
+        return;
+    }
+    return error.LibCKernel32LibNotFound;
+}
+
+fn findNativeMsvcIncludeDir(
+    self: *LibCInstallation,
+    args: FindNativeOptions,
+    sdk: *std.zig.WindowsSdk,
+) FindError!void {
+    const allocator = args.allocator;
+
+    const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCStdLibHeaderNotFound;
+    const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound;
+    const up2 = fs.path.dirname(up1) orelse return error.LibCStdLibHeaderNotFound;
+
+    const dir_path = try fs.path.join(allocator, &[_][]const u8{ up2, "include" });
+    errdefer allocator.free(dir_path);
+
+    var dir = fs.cwd().openDir(dir_path, .{}) catch |err| switch (err) {
+        error.FileNotFound,
+        error.NotDir,
+        error.NoDevice,
+        => return error.LibCStdLibHeaderNotFound,
+
+        else => return error.FileSystem,
+    };
+    defer dir.close();
+
+    dir.accessZ("vcruntime.h", .{}) catch |err| switch (err) {
+        error.FileNotFound => return error.LibCStdLibHeaderNotFound,
+        else => return error.FileSystem,
+    };
+
+    self.sys_include_dir = dir_path;
+}
+
+fn findNativeMsvcLibDir(
+    self: *LibCInstallation,
+    args: FindNativeOptions,
+    sdk: *std.zig.WindowsSdk,
+) FindError!void {
+    const allocator = args.allocator;
+    const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCRuntimeNotFound;
+    self.msvc_lib_dir = try allocator.dupe(u8, msvc_lib_dir);
+}
+
+pub const CCPrintFileNameOptions = struct {
+    allocator: Allocator,
+    search_basename: []const u8,
+    want_dirname: enum { full_path, only_dir },
+    verbose: bool = false,
+};
+
+/// caller owns returned memory
+fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 {
+    const allocator = args.allocator;
+
+    // Detect infinite loops.
+    var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
+        error.Unexpected => unreachable, // WASI-only
+        else => |e| return e,
+    };
+    defer env_map.deinit();
+    const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: {
+        if (std.mem.eql(u8, phase, "1")) {
+            try env_map.put(inf_loop_env_key, "2");
+            break :blk true;
+        } else {
+            return error.ZigIsTheCCompiler;
+        }
+    } else blk: {
+        try env_map.put(inf_loop_env_key, "1");
+        break :blk false;
+    };
+
+    var argv = std.ArrayList([]const u8).init(allocator);
+    defer argv.deinit();
+
+    const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={s}", .{args.search_basename});
+    defer allocator.free(arg1);
+
+    try appendCcExe(&argv, skip_cc_env_var);
+    try argv.append(arg1);
+
+    const run_res = std.ChildProcess.run(.{
+        .allocator = allocator,
+        .argv = argv.items,
+        .max_output_bytes = 1024 * 1024,
+        .env_map = &env_map,
+        // Some C compilers, such as Clang, are known to rely on argv[0] to find the path
+        // to their own executable, without even bothering to resolve PATH. This results in the message:
+        // error: unable to execute command: Executable "" doesn't exist!
+        // So we use the expandArg0 variant of ChildProcess to give them a helping hand.
+        .expand_arg0 = .expand,
+    }) catch |err| switch (err) {
+        error.OutOfMemory => return error.OutOfMemory,
+        else => return error.UnableToSpawnCCompiler,
+    };
+    defer {
+        allocator.free(run_res.stdout);
+        allocator.free(run_res.stderr);
+    }
+    switch (run_res.term) {
+        .Exited => |code| if (code != 0) {
+            printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr);
+            return error.CCompilerExitCode;
+        },
+        else => {
+            printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr);
+            return error.CCompilerCrashed;
+        },
+    }
+
+    var it = std.mem.tokenizeAny(u8, run_res.stdout, "\n\r");
+    const line = it.next() orelse return error.LibCRuntimeNotFound;
+    // When this command fails, it returns exit code 0 and duplicates the input file name.
+    // So we detect failure by checking if the output matches exactly the input.
+    if (std.mem.eql(u8, line, args.search_basename)) return error.LibCRuntimeNotFound;
+    switch (args.want_dirname) {
+        .full_path => return allocator.dupeZ(u8, line),
+        .only_dir => {
+            const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
+            return allocator.dupeZ(u8, dirname);
+        },
+    }
+}
+
+fn printVerboseInvocation(
+    argv: []const []const u8,
+    search_basename: ?[]const u8,
+    verbose: bool,
+    stderr: ?[]const u8,
+) void {
+    if (!verbose) return;
+
+    if (search_basename) |s| {
+        std.debug.print("Zig attempted to find the file '{s}' by executing this command:\n", .{s});
+    } else {
+        std.debug.print("Zig attempted to find the path to native system libc headers by executing this command:\n", .{});
+    }
+    for (argv, 0..) |arg, i| {
+        if (i != 0) std.debug.print(" ", .{});
+        std.debug.print("{s}", .{arg});
+    }
+    std.debug.print("\n", .{});
+    if (stderr) |s| {
+        std.debug.print("Output:\n==========\n{s}\n==========\n", .{s});
+    }
+}
+
+const Search = struct {
+    path: []const u8,
+    version: []const u8,
+};
+
+fn fillSearch(search_buf: *[2]Search, sdk: *std.zig.WindowsSdk) []Search {
+    var search_end: usize = 0;
+    if (sdk.windows10sdk) |windows10sdk| {
+        search_buf[search_end] = .{
+            .path = windows10sdk.path,
+            .version = windows10sdk.version,
+        };
+        search_end += 1;
+    }
+    if (sdk.windows81sdk) |windows81sdk| {
+        search_buf[search_end] = .{
+            .path = windows81sdk.path,
+            .version = windows81sdk.version,
+        };
+        search_end += 1;
+    }
+    return search_buf[0..search_end];
+}
+
+const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS";
+
+fn appendCcExe(args: *std.ArrayList([]const u8), skip_cc_env_var: bool) !void {
+    const default_cc_exe = if (is_windows) "cc.exe" else "cc";
+    try args.ensureUnusedCapacity(1);
+    if (skip_cc_env_var) {
+        args.appendAssumeCapacity(default_cc_exe);
+        return;
+    }
+    const cc_env_var = std.zig.EnvVar.CC.getPosix() orelse {
+        args.appendAssumeCapacity(default_cc_exe);
+        return;
+    };
+    // Respect space-separated flags to the C compiler.
+    var it = std.mem.tokenizeScalar(u8, cc_env_var, ' ');
+    while (it.next()) |arg| {
+        try args.append(arg);
+    }
+}
+
+const LibCInstallation = @This();
+const std = @import("std");
+const builtin = @import("builtin");
+const Target = std.Target;
+const fs = std.fs;
+const Allocator = std.mem.Allocator;
+
+const is_darwin = builtin.target.isDarwin();
+const is_windows = builtin.target.os.tag == .windows;
+const is_haiku = builtin.target.os.tag == .haiku;
+
+const log = std.log.scoped(.libc_installation);
lib/std/zig/target.zig
@@ -0,0 +1,117 @@
+pub const ArchOsAbi = struct {
+    arch: std.Target.Cpu.Arch,
+    os: std.Target.Os.Tag,
+    abi: std.Target.Abi,
+    os_ver: ?std.SemanticVersion = null,
+
+    // Minimum glibc version that provides support for the arch/os when ABI is GNU.
+    glibc_min: ?std.SemanticVersion = null,
+};
+
+pub const available_libcs = [_]ArchOsAbi{
+    .{ .arch = .aarch64_be, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 17, .patch = 0 } },
+    .{ .arch = .aarch64_be, .os = .linux, .abi = .musl },
+    .{ .arch = .aarch64_be, .os = .windows, .abi = .gnu },
+    .{ .arch = .aarch64, .os = .linux, .abi = .gnu },
+    .{ .arch = .aarch64, .os = .linux, .abi = .musl },
+    .{ .arch = .aarch64, .os = .windows, .abi = .gnu },
+    .{ .arch = .aarch64, .os = .macos, .abi = .none, .os_ver = .{ .major = 11, .minor = 0, .patch = 0 } },
+    .{ .arch = .armeb, .os = .linux, .abi = .gnueabi },
+    .{ .arch = .armeb, .os = .linux, .abi = .gnueabihf },
+    .{ .arch = .armeb, .os = .linux, .abi = .musleabi },
+    .{ .arch = .armeb, .os = .linux, .abi = .musleabihf },
+    .{ .arch = .armeb, .os = .windows, .abi = .gnu },
+    .{ .arch = .arm, .os = .linux, .abi = .gnueabi },
+    .{ .arch = .arm, .os = .linux, .abi = .gnueabihf },
+    .{ .arch = .arm, .os = .linux, .abi = .musleabi },
+    .{ .arch = .arm, .os = .linux, .abi = .musleabihf },
+    .{ .arch = .thumb, .os = .linux, .abi = .gnueabi },
+    .{ .arch = .thumb, .os = .linux, .abi = .gnueabihf },
+    .{ .arch = .thumb, .os = .linux, .abi = .musleabi },
+    .{ .arch = .thumb, .os = .linux, .abi = .musleabihf },
+    .{ .arch = .arm, .os = .windows, .abi = .gnu },
+    .{ .arch = .csky, .os = .linux, .abi = .gnueabi },
+    .{ .arch = .csky, .os = .linux, .abi = .gnueabihf },
+    .{ .arch = .x86, .os = .linux, .abi = .gnu },
+    .{ .arch = .x86, .os = .linux, .abi = .musl },
+    .{ .arch = .x86, .os = .windows, .abi = .gnu },
+    .{ .arch = .m68k, .os = .linux, .abi = .gnu },
+    .{ .arch = .m68k, .os = .linux, .abi = .musl },
+    .{ .arch = .mips64el, .os = .linux, .abi = .gnuabi64 },
+    .{ .arch = .mips64el, .os = .linux, .abi = .gnuabin32 },
+    .{ .arch = .mips64el, .os = .linux, .abi = .musl },
+    .{ .arch = .mips64, .os = .linux, .abi = .gnuabi64 },
+    .{ .arch = .mips64, .os = .linux, .abi = .gnuabin32 },
+    .{ .arch = .mips64, .os = .linux, .abi = .musl },
+    .{ .arch = .mipsel, .os = .linux, .abi = .gnueabi },
+    .{ .arch = .mipsel, .os = .linux, .abi = .gnueabihf },
+    .{ .arch = .mipsel, .os = .linux, .abi = .musl },
+    .{ .arch = .mips, .os = .linux, .abi = .gnueabi },
+    .{ .arch = .mips, .os = .linux, .abi = .gnueabihf },
+    .{ .arch = .mips, .os = .linux, .abi = .musl },
+    .{ .arch = .powerpc64le, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 19, .patch = 0 } },
+    .{ .arch = .powerpc64le, .os = .linux, .abi = .musl },
+    .{ .arch = .powerpc64, .os = .linux, .abi = .gnu },
+    .{ .arch = .powerpc64, .os = .linux, .abi = .musl },
+    .{ .arch = .powerpc, .os = .linux, .abi = .gnueabi },
+    .{ .arch = .powerpc, .os = .linux, .abi = .gnueabihf },
+    .{ .arch = .powerpc, .os = .linux, .abi = .musl },
+    .{ .arch = .riscv64, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 27, .patch = 0 } },
+    .{ .arch = .riscv64, .os = .linux, .abi = .musl },
+    .{ .arch = .s390x, .os = .linux, .abi = .gnu },
+    .{ .arch = .s390x, .os = .linux, .abi = .musl },
+    .{ .arch = .sparc, .os = .linux, .abi = .gnu },
+    .{ .arch = .sparc64, .os = .linux, .abi = .gnu },
+    .{ .arch = .wasm32, .os = .freestanding, .abi = .musl },
+    .{ .arch = .wasm32, .os = .wasi, .abi = .musl },
+    .{ .arch = .x86_64, .os = .linux, .abi = .gnu },
+    .{ .arch = .x86_64, .os = .linux, .abi = .gnux32 },
+    .{ .arch = .x86_64, .os = .linux, .abi = .musl },
+    .{ .arch = .x86_64, .os = .windows, .abi = .gnu },
+    .{ .arch = .x86_64, .os = .macos, .abi = .none, .os_ver = .{ .major = 10, .minor = 7, .patch = 0 } },
+};
+
+pub fn canBuildLibC(target: std.Target) bool {
+    for (available_libcs) |libc| {
+        if (target.cpu.arch == libc.arch and target.os.tag == libc.os and target.abi == libc.abi) {
+            if (target.os.tag == .macos) {
+                const ver = target.os.version_range.semver;
+                return ver.min.order(libc.os_ver.?) != .lt;
+            }
+            // Ensure glibc (aka *-linux-gnu) version is supported
+            if (target.isGnuLibC()) {
+                const min_glibc_ver = libc.glibc_min orelse return true;
+                const target_glibc_ver = target.os.version_range.linux.glibc;
+                return target_glibc_ver.order(min_glibc_ver) != .lt;
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+pub fn muslArchNameHeaders(arch: std.Target.Cpu.Arch) [:0]const u8 {
+    return switch (arch) {
+        .x86 => return "x86",
+        else => muslArchName(arch),
+    };
+}
+
+pub fn muslArchName(arch: std.Target.Cpu.Arch) [:0]const u8 {
+    switch (arch) {
+        .aarch64, .aarch64_be => return "aarch64",
+        .arm, .armeb, .thumb, .thumbeb => return "arm",
+        .x86 => return "i386",
+        .mips, .mipsel => return "mips",
+        .mips64el, .mips64 => return "mips64",
+        .powerpc => return "powerpc",
+        .powerpc64, .powerpc64le => return "powerpc64",
+        .riscv64 => return "riscv64",
+        .s390x => return "s390x",
+        .wasm32, .wasm64 => return "wasm",
+        .x86_64 => return "x86_64",
+        else => unreachable,
+    }
+}
+
+const std = @import("std");
src/windows_sdk.zig โ†’ lib/std/zig/WindowsSdk.zig
@@ -1,3 +1,8 @@
+windows10sdk: ?Windows10Sdk,
+windows81sdk: ?Windows81Sdk,
+msvc_lib_dir: ?[]const u8,
+
+const WindowsSdk = @This();
 const std = @import("std");
 const builtin = @import("builtin");
 
@@ -11,6 +16,69 @@ const version_major_minor_max_length = "255.255".len;
 // note(bratishkaerik): i think ProductVersion in registry (created by Visual Studio installer) also follows this rule
 const product_version_max_length = version_major_minor_max_length + ".65535".len;
 
+/// Find path and version of Windows 10 SDK and Windows 8.1 SDK, and find path to MSVC's `lib/` directory.
+/// Caller owns the result's fields.
+/// After finishing work, call `free(allocator)`.
+pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, NotFound, PathTooLong }!WindowsSdk {
+    if (builtin.os.tag != .windows) return error.NotFound;
+
+    //note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed
+    const roots_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, WINDOWS_KIT_REG_KEY) catch |err| switch (err) {
+        error.KeyNotFound => return error.NotFound,
+    };
+    defer roots_key.closeKey();
+
+    const windows10sdk: ?Windows10Sdk = blk: {
+        const windows10sdk = Windows10Sdk.find(allocator) catch |err| switch (err) {
+            error.Windows10SdkNotFound,
+            error.PathTooLong,
+            error.VersionTooLong,
+            => break :blk null,
+            error.OutOfMemory => return error.OutOfMemory,
+        };
+        const is_valid_version = windows10sdk.isValidVersion();
+        if (!is_valid_version) break :blk null;
+        break :blk windows10sdk;
+    };
+    errdefer if (windows10sdk) |*w| w.free(allocator);
+
+    const windows81sdk: ?Windows81Sdk = blk: {
+        const windows81sdk = Windows81Sdk.find(allocator, &roots_key) catch |err| switch (err) {
+            error.Windows81SdkNotFound => break :blk null,
+            error.PathTooLong => break :blk null,
+            error.VersionTooLong => break :blk null,
+            error.OutOfMemory => return error.OutOfMemory,
+        };
+        // no check
+        break :blk windows81sdk;
+    };
+    errdefer if (windows81sdk) |*w| w.free(allocator);
+
+    const msvc_lib_dir: ?[]const u8 = MsvcLibDir.find(allocator) catch |err| switch (err) {
+        error.MsvcLibDirNotFound => null,
+        error.OutOfMemory => return error.OutOfMemory,
+    };
+    errdefer allocator.free(msvc_lib_dir);
+
+    return WindowsSdk{
+        .windows10sdk = windows10sdk,
+        .windows81sdk = windows81sdk,
+        .msvc_lib_dir = msvc_lib_dir,
+    };
+}
+
+pub fn free(self: *const WindowsSdk, allocator: std.mem.Allocator) void {
+    if (self.windows10sdk) |*w10sdk| {
+        w10sdk.free(allocator);
+    }
+    if (self.windows81sdk) |*w81sdk| {
+        w81sdk.free(allocator);
+    }
+    if (self.msvc_lib_dir) |msvc_lib_dir| {
+        allocator.free(msvc_lib_dir);
+    }
+}
+
 /// Iterates via `iterator` and collects all folders with names starting with `optional_prefix`
 /// and similar to SemVer. Returns slice of folder names sorted in descending order.
 /// Caller owns result.
@@ -508,75 +576,6 @@ pub const Windows81Sdk = struct {
     }
 };
 
-pub const ZigWindowsSDK = struct {
-    windows10sdk: ?Windows10Sdk,
-    windows81sdk: ?Windows81Sdk,
-    msvc_lib_dir: ?[]const u8,
-
-    /// Find path and version of Windows 10 SDK and Windows 8.1 SDK, and find path to MSVC's `lib/` directory.
-    /// Caller owns the result's fields.
-    /// After finishing work, call `free(allocator)`.
-    pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, NotFound, PathTooLong }!ZigWindowsSDK {
-        if (builtin.os.tag != .windows) return error.NotFound;
-
-        //note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed
-        const roots_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, WINDOWS_KIT_REG_KEY) catch |err| switch (err) {
-            error.KeyNotFound => return error.NotFound,
-        };
-        defer roots_key.closeKey();
-
-        const windows10sdk: ?Windows10Sdk = blk: {
-            const windows10sdk = Windows10Sdk.find(allocator) catch |err| switch (err) {
-                error.Windows10SdkNotFound,
-                error.PathTooLong,
-                error.VersionTooLong,
-                => break :blk null,
-                error.OutOfMemory => return error.OutOfMemory,
-            };
-            const is_valid_version = windows10sdk.isValidVersion();
-            if (!is_valid_version) break :blk null;
-            break :blk windows10sdk;
-        };
-        errdefer if (windows10sdk) |*w| w.free(allocator);
-
-        const windows81sdk: ?Windows81Sdk = blk: {
-            const windows81sdk = Windows81Sdk.find(allocator, &roots_key) catch |err| switch (err) {
-                error.Windows81SdkNotFound => break :blk null,
-                error.PathTooLong => break :blk null,
-                error.VersionTooLong => break :blk null,
-                error.OutOfMemory => return error.OutOfMemory,
-            };
-            // no check
-            break :blk windows81sdk;
-        };
-        errdefer if (windows81sdk) |*w| w.free(allocator);
-
-        const msvc_lib_dir: ?[]const u8 = MsvcLibDir.find(allocator) catch |err| switch (err) {
-            error.MsvcLibDirNotFound => null,
-            error.OutOfMemory => return error.OutOfMemory,
-        };
-        errdefer allocator.free(msvc_lib_dir);
-
-        return ZigWindowsSDK{
-            .windows10sdk = windows10sdk,
-            .windows81sdk = windows81sdk,
-            .msvc_lib_dir = msvc_lib_dir,
-        };
-    }
-
-    pub fn free(self: *const ZigWindowsSDK, allocator: std.mem.Allocator) void {
-        if (self.windows10sdk) |*w10sdk| {
-            w10sdk.free(allocator);
-        }
-        if (self.windows81sdk) |*w81sdk| {
-            w81sdk.free(allocator);
-        }
-        if (self.msvc_lib_dir) |msvc_lib_dir| {
-            allocator.free(msvc_lib_dir);
-        }
-    }
-};
-
 const MsvcLibDir = struct {
     fn findInstancesDirViaCLSID(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {
         const setup_configuration_clsid = "{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}";
lib/std/Target.zig
@@ -2755,6 +2755,22 @@ fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool {
     }
 }
 
+pub fn osArchName(target: std.Target) [:0]const u8 {
+    return switch (target.os.tag) {
+        .linux => switch (target.cpu.arch) {
+            .arm, .armeb, .thumb, .thumbeb => "arm",
+            .aarch64, .aarch64_be, .aarch64_32 => "aarch64",
+            .mips, .mipsel, .mips64, .mips64el => "mips",
+            .powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc",
+            .riscv32, .riscv64 => "riscv",
+            .sparc, .sparcel, .sparc64 => "sparc",
+            .x86, .x86_64 => "x86",
+            else => @tagName(target.cpu.arch),
+        },
+        else => @tagName(target.cpu.arch),
+    };
+}
+
 const Target = @This();
 const std = @import("std.zig");
 const builtin = @import("builtin");
lib/std/zig.zig
@@ -14,6 +14,10 @@ pub const system = @import("zig/system.zig");
 pub const CrossTarget = std.Target.Query;
 pub const BuiltinFn = @import("zig/BuiltinFn.zig");
 pub const AstRlAnnotate = @import("zig/AstRlAnnotate.zig");
+pub const LibCInstallation = @import("zig/LibCInstallation.zig");
+pub const WindowsSdk = @import("zig/WindowsSdk.zig");
+pub const LibCDirs = @import("zig/LibCDirs.zig");
+pub const target = @import("zig/target.zig");
 
 // Character literal parsing
 pub const ParsedCharLiteral = string_literal.ParsedCharLiteral;
@@ -142,10 +146,10 @@ pub const BinNameOptions = struct {
 /// Returns the standard file system basename of a binary generated by the Zig compiler.
 pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 {
     const root_name = options.root_name;
-    const target = options.target;
-    switch (target.ofmt) {
+    const t = options.target;
+    switch (t.ofmt) {
         .coff => switch (options.output_mode) {
-            .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
+            .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, t.exeFileExt() }),
             .Lib => {
                 const suffix = switch (options.link_mode orelse .Static) {
                     .Static => ".lib",
@@ -160,16 +164,16 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe
             .Lib => {
                 switch (options.link_mode orelse .Static) {
                     .Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
-                        target.libPrefix(), root_name,
+                        t.libPrefix(), root_name,
                     }),
                     .Dynamic => {
                         if (options.version) |ver| {
                             return std.fmt.allocPrint(allocator, "{s}{s}.so.{d}.{d}.{d}", .{
-                                target.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
+                                t.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
                             });
                         } else {
                             return std.fmt.allocPrint(allocator, "{s}{s}.so", .{
-                                target.libPrefix(), root_name,
+                                t.libPrefix(), root_name,
                             });
                         }
                     },
@@ -182,16 +186,16 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe
             .Lib => {
                 switch (options.link_mode orelse .Static) {
                     .Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
-                        target.libPrefix(), root_name,
+                        t.libPrefix(), root_name,
                     }),
                     .Dynamic => {
                         if (options.version) |ver| {
                             return std.fmt.allocPrint(allocator, "{s}{s}.{d}.{d}.{d}.dylib", .{
-                                target.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
+                                t.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
                             });
                         } else {
                             return std.fmt.allocPrint(allocator, "{s}{s}.dylib", .{
-                                target.libPrefix(), root_name,
+                                t.libPrefix(), root_name,
                             });
                         }
                     },
@@ -200,11 +204,11 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe
             .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
         },
         .wasm => switch (options.output_mode) {
-            .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
+            .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, t.exeFileExt() }),
             .Lib => {
                 switch (options.link_mode orelse .Static) {
                     .Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
-                        target.libPrefix(), root_name,
+                        t.libPrefix(), root_name,
                     }),
                     .Dynamic => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}),
                 }
@@ -218,10 +222,10 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe
         .plan9 => switch (options.output_mode) {
             .Exe => return allocator.dupe(u8, root_name),
             .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{
-                root_name, target.ofmt.fileExt(target.cpu.arch),
+                root_name, t.ofmt.fileExt(t.cpu.arch),
             }),
             .Lib => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
-                target.libPrefix(), root_name,
+                t.libPrefix(), root_name,
             }),
         },
         .nvptx => return std.fmt.allocPrint(allocator, "{s}.ptx", .{root_name}),
@@ -900,15 +904,117 @@ pub fn putAstErrorsIntoBundle(
     try wip_errors.addZirErrorMessages(zir, tree, tree.source, path);
 }
 
+pub fn resolveTargetQueryOrFatal(target_query: std.Target.Query) std.Target {
+    return std.zig.system.resolveTargetQuery(target_query) catch |err|
+        fatal("unable to resolve target: {s}", .{@errorName(err)});
+}
+
+pub fn parseTargetQueryOrReportFatalError(
+    allocator: Allocator,
+    opts: std.Target.Query.ParseOptions,
+) std.Target.Query {
+    var opts_with_diags = opts;
+    var diags: std.Target.Query.ParseOptions.Diagnostics = .{};
+    if (opts_with_diags.diagnostics == null) {
+        opts_with_diags.diagnostics = &diags;
+    }
+    return std.Target.Query.parse(opts_with_diags) catch |err| switch (err) {
+        error.UnknownCpuModel => {
+            help: {
+                var help_text = std.ArrayList(u8).init(allocator);
+                defer help_text.deinit();
+                for (diags.arch.?.allCpuModels()) |cpu| {
+                    help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help;
+                }
+                std.log.info("available CPUs for architecture '{s}':\n{s}", .{
+                    @tagName(diags.arch.?), help_text.items,
+                });
+            }
+            fatal("unknown CPU: '{s}'", .{diags.cpu_name.?});
+        },
+        error.UnknownCpuFeature => {
+            help: {
+                var help_text = std.ArrayList(u8).init(allocator);
+                defer help_text.deinit();
+                for (diags.arch.?.allFeaturesList()) |feature| {
+                    help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help;
+                }
+                std.log.info("available CPU features for architecture '{s}':\n{s}", .{
+                    @tagName(diags.arch.?), help_text.items,
+                });
+            }
+            fatal("unknown CPU feature: '{s}'", .{diags.unknown_feature_name.?});
+        },
+        error.UnknownObjectFormat => {
+            help: {
+                var help_text = std.ArrayList(u8).init(allocator);
+                defer help_text.deinit();
+                inline for (@typeInfo(std.Target.ObjectFormat).Enum.fields) |field| {
+                    help_text.writer().print(" {s}\n", .{field.name}) catch break :help;
+                }
+                std.log.info("available object formats:\n{s}", .{help_text.items});
+            }
+            fatal("unknown object format: '{s}'", .{opts.object_format.?});
+        },
+        else => |e| fatal("unable to parse target query '{s}': {s}", .{
+            opts.arch_os_abi, @errorName(e),
+        }),
+    };
+}
+
+pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
+    std.log.err(format, args);
+    std.process.exit(1);
+}
+
+/// Collects all the environment variables that Zig could possibly inspect, so
+/// that we can do reflection on this and print them with `zig env`.
+pub const EnvVar = enum {
+    ZIG_GLOBAL_CACHE_DIR,
+    ZIG_LOCAL_CACHE_DIR,
+    ZIG_LIB_DIR,
+    ZIG_LIBC,
+    ZIG_BUILD_RUNNER,
+    ZIG_VERBOSE_LINK,
+    ZIG_VERBOSE_CC,
+    ZIG_BTRFS_WORKAROUND,
+    ZIG_DEBUG_CMD,
+    CC,
+    NO_COLOR,
+    XDG_CACHE_HOME,
+    HOME,
+
+    pub fn isSet(comptime ev: EnvVar) bool {
+        return std.process.hasEnvVarConstant(@tagName(ev));
+    }
+
+    pub fn get(ev: EnvVar, arena: std.mem.Allocator) !?[]u8 {
+        if (std.process.getEnvVarOwned(arena, @tagName(ev))) |value| {
+            return value;
+        } else |err| switch (err) {
+            error.EnvironmentVariableNotFound => return null,
+            else => |e| return e,
+        }
+    }
+
+    pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 {
+        return std.os.getenvZ(@tagName(ev));
+    }
+};
+
 test {
     _ = Ast;
     _ = AstRlAnnotate;
     _ = BuiltinFn;
     _ = Client;
     _ = ErrorBundle;
+    _ = LibCDirs;
+    _ = LibCInstallation;
     _ = Server;
+    _ = WindowsSdk;
     _ = number_literal;
     _ = primitives;
     _ = string_literal;
     _ = system;
+    _ = target;
 }
src/link/MachO.zig
@@ -4616,13 +4616,7 @@ const SystemLib = struct {
     must_link: bool = false,
 };
 
-/// The filesystem layout of darwin SDK elements.
-pub const SdkLayout = enum {
-    /// macOS SDK layout: TOP { /usr/include, /usr/lib, /System/Library/Frameworks }.
-    sdk,
-    /// Shipped libc layout: TOP { /lib/libc/include,  /lib/libc/darwin, <NONE> }.
-    vendored,
-};
+pub const SdkLayout = std.zig.LibCDirs.DarwinSdkLayout;
 
 const UndefinedTreatment = enum {
     @"error",
src/Compilation.zig
@@ -19,7 +19,7 @@ const link = @import("link.zig");
 const tracy = @import("tracy.zig");
 const trace = tracy.trace;
 const build_options = @import("build_options");
-const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
+const LibCInstallation = std.zig.LibCInstallation;
 const glibc = @import("glibc.zig");
 const musl = @import("musl.zig");
 const mingw = @import("mingw.zig");
@@ -1232,7 +1232,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
 
         const link_libc = options.config.link_libc;
 
-        const libc_dirs = try detectLibCIncludeDirs(
+        const libc_dirs = try std.zig.LibCDirs.detect(
             arena,
             options.zig_lib_directory.path.?,
             options.root_mod.resolved_target.result,
@@ -1250,7 +1250,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
         // only relevant differences would be things like `#define` constants being
         // different in the MinGW headers vs the MSVC headers, but any such
         // differences would likely be a MinGW bug.
-        const rc_dirs = b: {
+        const rc_dirs: std.zig.LibCDirs = b: {
             // Set the includes to .none here when there are no rc files to compile
             var includes = if (options.rc_source_files.len > 0) options.rc_includes else .none;
             const target = options.root_mod.resolved_target.result;
@@ -1265,7 +1265,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
                 }
             }
             while (true) switch (includes) {
-                .any, .msvc => break :b detectLibCIncludeDirs(
+                .any, .msvc => break :b std.zig.LibCDirs.detect(
                     arena,
                     options.zig_lib_directory.path.?,
                     .{
@@ -1287,13 +1287,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
                     }
                     return err;
                 },
-                .gnu => break :b try detectLibCFromBuilding(arena, options.zig_lib_directory.path.?, .{
+                .gnu => break :b try std.zig.LibCDirs.detectFromBuilding(arena, options.zig_lib_directory.path.?, .{
                     .cpu = target.cpu,
                     .os = target.os,
                     .abi = .gnu,
                     .ofmt = target.ofmt,
                 }),
-                .none => break :b LibCDirs{
+                .none => break :b .{
                     .libc_include_dir_list = &[0][]u8{},
                     .libc_installation = null,
                     .libc_framework_dir_list = &.{},
@@ -1772,7 +1772,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
         // If we need to build glibc for the target, add work items for it.
         // We go through the work queue so that building can be done in parallel.
         if (comp.wantBuildGLibCFromSource()) {
-            if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
+            if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
 
             if (glibc.needsCrtiCrtn(target)) {
                 try comp.work_queue.write(&[_]Job{
@@ -1787,7 +1787,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
             });
         }
         if (comp.wantBuildMuslFromSource()) {
-            if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
+            if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
 
             try comp.work_queue.ensureUnusedCapacity(6);
             if (musl.needsCrtiCrtn(target)) {
@@ -1808,7 +1808,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
         }
 
         if (comp.wantBuildWasiLibcFromSource()) {
-            if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
+            if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
 
             // worst-case we need all components
             try comp.work_queue.ensureUnusedCapacity(comp.wasi_emulated_libs.len + 2);
@@ -1825,7 +1825,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
         }
 
         if (comp.wantBuildMinGWFromSource()) {
-            if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
+            if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
 
             const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o };
             try comp.work_queue.ensureUnusedCapacity(2);
@@ -5830,224 +5830,6 @@ test "classifyFileExt" {
     try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig"));
 }
 
-const LibCDirs = struct {
-    libc_include_dir_list: []const []const u8,
-    libc_installation: ?*const LibCInstallation,
-    libc_framework_dir_list: []const []const u8,
-    sysroot: ?[]const u8,
-    darwin_sdk_layout: ?link.File.MachO.SdkLayout,
-};
-
-fn getZigShippedLibCIncludeDirsDarwin(arena: Allocator, zig_lib_dir: []const u8) !LibCDirs {
-    const s = std.fs.path.sep_str;
-    const list = try arena.alloc([]const u8, 1);
-    list[0] = try std.fmt.allocPrint(
-        arena,
-        "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-macos-any",
-        .{zig_lib_dir},
-    );
-    return LibCDirs{
-        .libc_include_dir_list = list,
-        .libc_installation = null,
-        .libc_framework_dir_list = &.{},
-        .sysroot = null,
-        .darwin_sdk_layout = .vendored,
-    };
-}
-
-pub fn detectLibCIncludeDirs(
-    arena: Allocator,
-    zig_lib_dir: []const u8,
-    target: Target,
-    is_native_abi: bool,
-    link_libc: bool,
-    libc_installation: ?*const LibCInstallation,
-) !LibCDirs {
-    if (!link_libc) {
-        return LibCDirs{
-            .libc_include_dir_list = &[0][]u8{},
-            .libc_installation = null,
-            .libc_framework_dir_list = &.{},
-            .sysroot = null,
-            .darwin_sdk_layout = null,
-        };
-    }
-
-    if (libc_installation) |lci| {
-        return detectLibCFromLibCInstallation(arena, target, lci);
-    }
-
-    // If linking system libraries and targeting the native abi, default to
-    // using the system libc installation.
-    if (is_native_abi and !target.isMinGW()) {
-        const libc = try arena.create(LibCInstallation);
-        libc.* = LibCInstallation.findNative(.{ .allocator = arena, .target = target }) catch |err| switch (err) {
-            error.CCompilerExitCode,
-            error.CCompilerCrashed,
-            error.CCompilerCannotFindHeaders,
-            error.UnableToSpawnCCompiler,
-            error.DarwinSdkNotFound,
-            => |e| {
-                // We tried to integrate with the native system C compiler,
-                // however, it is not installed. So we must rely on our bundled
-                // libc files.
-                if (target_util.canBuildLibC(target)) {
-                    return detectLibCFromBuilding(arena, zig_lib_dir, target);
-                }
-                return e;
-            },
-            else => |e| return e,
-        };
-        return detectLibCFromLibCInstallation(arena, target, libc);
-    }
-
-    // If not linking system libraries, build and provide our own libc by
-    // default if possible.
-    if (target_util.canBuildLibC(target)) {
-        return detectLibCFromBuilding(arena, zig_lib_dir, target);
-    }
-
-    // If zig can't build the libc for the target and we are targeting the
-    // native abi, fall back to using the system libc installation.
-    // On windows, instead of the native (mingw) abi, we want to check
-    // for the MSVC abi as a fallback.
-    const use_system_abi = if (builtin.target.os.tag == .windows)
-        target.abi == .msvc
-    else
-        is_native_abi;
-
-    if (use_system_abi) {
-        const libc = try arena.create(LibCInstallation);
-        libc.* = try LibCInstallation.findNative(.{ .allocator = arena, .verbose = true, .target = target });
-        return detectLibCFromLibCInstallation(arena, target, libc);
-    }
-
-    return LibCDirs{
-        .libc_include_dir_list = &[0][]u8{},
-        .libc_installation = null,
-        .libc_framework_dir_list = &.{},
-        .sysroot = null,
-        .darwin_sdk_layout = null,
-    };
-}
-
-fn detectLibCFromLibCInstallation(arena: Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs {
-    var list = try std.ArrayList([]const u8).initCapacity(arena, 5);
-    var framework_list = std.ArrayList([]const u8).init(arena);
-
-    list.appendAssumeCapacity(lci.include_dir.?);
-
-    const is_redundant = mem.eql(u8, lci.sys_include_dir.?, lci.include_dir.?);
-    if (!is_redundant) list.appendAssumeCapacity(lci.sys_include_dir.?);
-
-    if (target.os.tag == .windows) {
-        if (std.fs.path.dirname(lci.sys_include_dir.?)) |sys_include_dir_parent| {
-            // This include path will only exist when the optional "Desktop development with C++"
-            // is installed. It contains headers, .rc files, and resources. It is especially
-            // necessary when working with Windows resources.
-            const atlmfc_dir = try std.fs.path.join(arena, &[_][]const u8{ sys_include_dir_parent, "atlmfc", "include" });
-            list.appendAssumeCapacity(atlmfc_dir);
-        }
-        if (std.fs.path.dirname(lci.include_dir.?)) |include_dir_parent| {
-            const um_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "um" });
-            list.appendAssumeCapacity(um_dir);
-
-            const shared_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "shared" });
-            list.appendAssumeCapacity(shared_dir);
-        }
-    }
-    if (target.os.tag == .haiku) {
-        const include_dir_path = lci.include_dir orelse return error.LibCInstallationNotAvailable;
-        const os_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os" });
-        list.appendAssumeCapacity(os_dir);
-        // Errors.h
-        const os_support_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os/support" });
-        list.appendAssumeCapacity(os_support_dir);
-
-        const config_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "config" });
-        list.appendAssumeCapacity(config_dir);
-    }
-
-    var sysroot: ?[]const u8 = null;
-
-    if (target.isDarwin()) d: {
-        const down1 = std.fs.path.dirname(lci.sys_include_dir.?) orelse break :d;
-        const down2 = std.fs.path.dirname(down1) orelse break :d;
-        try framework_list.append(try std.fs.path.join(arena, &.{ down2, "System", "Library", "Frameworks" }));
-        sysroot = down2;
-    }
-
-    return LibCDirs{
-        .libc_include_dir_list = list.items,
-        .libc_installation = lci,
-        .libc_framework_dir_list = framework_list.items,
-        .sysroot = sysroot,
-        .darwin_sdk_layout = if (sysroot == null) null else .sdk,
-    };
-}
-
-fn detectLibCFromBuilding(
-    arena: Allocator,
-    zig_lib_dir: []const u8,
-    target: std.Target,
-) !LibCDirs {
-    if (target.isDarwin())
-        return getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir);
-
-    const generic_name = target_util.libCGenericName(target);
-    // Some architectures are handled by the same set of headers.
-    const arch_name = if (target.abi.isMusl())
-        musl.archNameHeaders(target.cpu.arch)
-    else if (target.cpu.arch.isThumb())
-        // ARM headers are valid for Thumb too.
-        switch (target.cpu.arch) {
-            .thumb => "arm",
-            .thumbeb => "armeb",
-            else => unreachable,
-        }
-    else
-        @tagName(target.cpu.arch);
-    const os_name = @tagName(target.os.tag);
-    // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name.
-    const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi);
-    const s = std.fs.path.sep_str;
-    const arch_include_dir = try std.fmt.allocPrint(
-        arena,
-        "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}",
-        .{ zig_lib_dir, arch_name, os_name, abi_name },
-    );
-    const generic_include_dir = try std.fmt.allocPrint(
-        arena,
-        "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}",
-        .{ zig_lib_dir, generic_name },
-    );
-    const generic_arch_name = target_util.osArchName(target);
-    const arch_os_include_dir = try std.fmt.allocPrint(
-        arena,
-        "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any",
-        .{ zig_lib_dir, generic_arch_name, os_name },
-    );
-    const generic_os_include_dir = try std.fmt.allocPrint(
-        arena,
-        "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any",
-        .{ zig_lib_dir, os_name },
-    );
-
-    const list = try arena.alloc([]const u8, 4);
-    list[0] = arch_include_dir;
-    list[1] = generic_include_dir;
-    list[2] = arch_os_include_dir;
-    list[3] = generic_os_include_dir;
-
-    return LibCDirs{
-        .libc_include_dir_list = list,
-        .libc_installation = null,
-        .libc_framework_dir_list = &.{},
-        .sysroot = null,
-        .darwin_sdk_layout = .vendored,
-    };
-}
-
 pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 {
     if (comp.wantBuildGLibCFromSource() or
         comp.wantBuildMuslFromSource() or
src/glibc.zig
@@ -7,7 +7,6 @@ const path = fs.path;
 const assert = std.debug.assert;
 const Version = std.SemanticVersion;
 
-const target_util = @import("target.zig");
 const Compilation = @import("Compilation.zig");
 const build_options = @import("build_options");
 const trace = @import("tracy.zig").trace;
@@ -21,7 +20,7 @@ pub const Lib = struct {
 
 pub const ABI = struct {
     all_versions: []const Version, // all defined versions (one abilist from v2.0.0 up to current)
-    all_targets: []const target_util.ArchOsAbi,
+    all_targets: []const std.zig.target.ArchOsAbi,
     /// The bytes from the file verbatim, starting from the u16 number
     /// of function inclusions.
     inclusions: []const u8,
@@ -103,7 +102,7 @@ pub fn loadMetaData(gpa: Allocator, contents: []const u8) LoadMetaDataError!*ABI
         const targets_len = contents[index];
         index += 1;
 
-        const targets = try arena.alloc(target_util.ArchOsAbi, targets_len);
+        const targets = try arena.alloc(std.zig.target.ArchOsAbi, targets_len);
         var i: u8 = 0;
         while (i < targets.len) : (i += 1) {
             const target_name = mem.sliceTo(contents[index..], 0);
@@ -512,7 +511,7 @@ fn add_include_dirs(comp: *Compilation, arena: Allocator, args: *std.ArrayList([
     try args.append("-I");
     try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "generic-glibc"));
 
-    const arch_name = target_util.osArchName(target);
+    const arch_name = target.osArchName();
     try args.append("-I");
     try args.append(try std.fmt.allocPrint(arena, "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-linux-any", .{
         comp.zig_lib_directory.path.?, arch_name,
@@ -726,7 +725,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: *std.Progress.Node) !vo
             break i;
         }
     } else {
-        unreachable; // target_util.available_libcs prevents us from getting here
+        unreachable; // std.zig.target.available_libcs prevents us from getting here
     };
 
     const target_ver_index = for (metadata.all_versions, 0..) |ver, i| {
src/introspect.zig
@@ -84,14 +84,14 @@ pub fn resolveGlobalCacheDir(allocator: mem.Allocator) ![]u8 {
     if (builtin.os.tag == .wasi)
         @compileError("on WASI the global cache dir must be resolved with preopens");
 
-    if (try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(allocator)) |value| return value;
+    if (try std.zig.EnvVar.ZIG_GLOBAL_CACHE_DIR.get(allocator)) |value| return value;
 
     const appname = "zig";
 
     if (builtin.os.tag != .windows) {
-        if (EnvVar.XDG_CACHE_HOME.getPosix()) |cache_root| {
+        if (std.zig.EnvVar.XDG_CACHE_HOME.getPosix()) |cache_root| {
             return fs.path.join(allocator, &[_][]const u8{ cache_root, appname });
-        } else if (EnvVar.HOME.getPosix()) |home| {
+        } else if (std.zig.EnvVar.HOME.getPosix()) |home| {
             return fs.path.join(allocator, &[_][]const u8{ home, ".cache", appname });
         }
     }
@@ -141,41 +141,3 @@ pub fn resolvePath(
 pub fn isUpDir(p: []const u8) bool {
     return mem.startsWith(u8, p, "..") and (p.len == 2 or p[2] == fs.path.sep);
 }
-
-/// Collects all the environment variables that Zig could possibly inspect, so
-/// that we can do reflection on this and print them with `zig env`.
-pub const EnvVar = enum {
-    ZIG_GLOBAL_CACHE_DIR,
-    ZIG_LOCAL_CACHE_DIR,
-    ZIG_LIB_DIR,
-    ZIG_LIBC,
-    ZIG_BUILD_RUNNER,
-    ZIG_VERBOSE_LINK,
-    ZIG_VERBOSE_CC,
-    ZIG_BTRFS_WORKAROUND,
-    ZIG_DEBUG_CMD,
-    CC,
-    NO_COLOR,
-    XDG_CACHE_HOME,
-    HOME,
-
-    pub fn isSet(comptime ev: EnvVar) bool {
-        return std.process.hasEnvVarConstant(@tagName(ev));
-    }
-
-    pub fn get(ev: EnvVar, arena: mem.Allocator) !?[]u8 {
-        // Env vars aren't used in the bootstrap stage.
-        if (build_options.only_c) return null;
-
-        if (std.process.getEnvVarOwned(arena, @tagName(ev))) |value| {
-            return value;
-        } else |err| switch (err) {
-            error.EnvironmentVariableNotFound => return null,
-            else => |e| return e,
-        }
-    }
-
-    pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 {
-        return std.os.getenvZ(@tagName(ev));
-    }
-};
src/libc_installation.zig
@@ -1,712 +0,0 @@
-const std = @import("std");
-const builtin = @import("builtin");
-const Target = std.Target;
-const fs = std.fs;
-const Allocator = std.mem.Allocator;
-
-const is_darwin = builtin.target.isDarwin();
-const is_windows = builtin.target.os.tag == .windows;
-const is_haiku = builtin.target.os.tag == .haiku;
-
-const log = std.log.scoped(.libc_installation);
-
-const ZigWindowsSDK = @import("windows_sdk.zig").ZigWindowsSDK;
-const EnvVar = @import("introspect.zig").EnvVar;
-
-/// See the render function implementation for documentation of the fields.
-pub const LibCInstallation = struct {
-    include_dir: ?[]const u8 = null,
-    sys_include_dir: ?[]const u8 = null,
-    crt_dir: ?[]const u8 = null,
-    msvc_lib_dir: ?[]const u8 = null,
-    kernel32_lib_dir: ?[]const u8 = null,
-    gcc_dir: ?[]const u8 = null,
-
-    pub const FindError = error{
-        OutOfMemory,
-        FileSystem,
-        UnableToSpawnCCompiler,
-        CCompilerExitCode,
-        CCompilerCrashed,
-        CCompilerCannotFindHeaders,
-        LibCRuntimeNotFound,
-        LibCStdLibHeaderNotFound,
-        LibCKernel32LibNotFound,
-        UnsupportedArchitecture,
-        WindowsSdkNotFound,
-        DarwinSdkNotFound,
-        ZigIsTheCCompiler,
-    };
-
-    pub fn parse(
-        allocator: Allocator,
-        libc_file: []const u8,
-        target: std.Target,
-    ) !LibCInstallation {
-        var self: LibCInstallation = .{};
-
-        const fields = std.meta.fields(LibCInstallation);
-        const FoundKey = struct {
-            found: bool,
-            allocated: ?[:0]u8,
-        };
-        var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** fields.len;
-        errdefer {
-            self = .{};
-            for (found_keys) |found_key| {
-                if (found_key.allocated) |s| allocator.free(s);
-            }
-        }
-
-        const contents = try std.fs.cwd().readFileAlloc(allocator, libc_file, std.math.maxInt(usize));
-        defer allocator.free(contents);
-
-        var it = std.mem.tokenizeScalar(u8, contents, '\n');
-        while (it.next()) |line| {
-            if (line.len == 0 or line[0] == '#') continue;
-            var line_it = std.mem.splitScalar(u8, line, '=');
-            const name = line_it.first();
-            const value = line_it.rest();
-            inline for (fields, 0..) |field, i| {
-                if (std.mem.eql(u8, name, field.name)) {
-                    found_keys[i].found = true;
-                    if (value.len == 0) {
-                        @field(self, field.name) = null;
-                    } else {
-                        found_keys[i].allocated = try allocator.dupeZ(u8, value);
-                        @field(self, field.name) = found_keys[i].allocated;
-                    }
-                    break;
-                }
-            }
-        }
-        inline for (fields, 0..) |field, i| {
-            if (!found_keys[i].found) {
-                log.err("missing field: {s}\n", .{field.name});
-                return error.ParseError;
-            }
-        }
-        if (self.include_dir == null) {
-            log.err("include_dir may not be empty\n", .{});
-            return error.ParseError;
-        }
-        if (self.sys_include_dir == null) {
-            log.err("sys_include_dir may not be empty\n", .{});
-            return error.ParseError;
-        }
-
-        const os_tag = target.os.tag;
-        if (self.crt_dir == null and !target.isDarwin()) {
-            log.err("crt_dir may not be empty for {s}\n", .{@tagName(os_tag)});
-            return error.ParseError;
-        }
-
-        if (self.msvc_lib_dir == null and os_tag == .windows and target.abi == .msvc) {
-            log.err("msvc_lib_dir may not be empty for {s}-{s}\n", .{
-                @tagName(os_tag),
-                @tagName(target.abi),
-            });
-            return error.ParseError;
-        }
-        if (self.kernel32_lib_dir == null and os_tag == .windows and target.abi == .msvc) {
-            log.err("kernel32_lib_dir may not be empty for {s}-{s}\n", .{
-                @tagName(os_tag),
-                @tagName(target.abi),
-            });
-            return error.ParseError;
-        }
-
-        if (self.gcc_dir == null and os_tag == .haiku) {
-            log.err("gcc_dir may not be empty for {s}\n", .{@tagName(os_tag)});
-            return error.ParseError;
-        }
-
-        return self;
-    }
-
-    pub fn render(self: LibCInstallation, out: anytype) !void {
-        @setEvalBranchQuota(4000);
-        const include_dir = self.include_dir orelse "";
-        const sys_include_dir = self.sys_include_dir orelse "";
-        const crt_dir = self.crt_dir orelse "";
-        const msvc_lib_dir = self.msvc_lib_dir orelse "";
-        const kernel32_lib_dir = self.kernel32_lib_dir orelse "";
-        const gcc_dir = self.gcc_dir orelse "";
-
-        try out.print(
-            \\# The directory that contains `stdlib.h`.
-            \\# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null`
-            \\include_dir={s}
-            \\
-            \\# The system-specific include directory. May be the same as `include_dir`.
-            \\# On Windows it's the directory that includes `vcruntime.h`.
-            \\# On POSIX it's the directory that includes `sys/errno.h`.
-            \\sys_include_dir={s}
-            \\
-            \\# The directory that contains `crt1.o` or `crt2.o`.
-            \\# On POSIX, can be found with `cc -print-file-name=crt1.o`.
-            \\# Not needed when targeting MacOS.
-            \\crt_dir={s}
-            \\
-            \\# The directory that contains `vcruntime.lib`.
-            \\# Only needed when targeting MSVC on Windows.
-            \\msvc_lib_dir={s}
-            \\
-            \\# The directory that contains `kernel32.lib`.
-            \\# Only needed when targeting MSVC on Windows.
-            \\kernel32_lib_dir={s}
-            \\
-            \\# The directory that contains `crtbeginS.o` and `crtendS.o`
-            \\# Only needed when targeting Haiku.
-            \\gcc_dir={s}
-            \\
-        , .{
-            include_dir,
-            sys_include_dir,
-            crt_dir,
-            msvc_lib_dir,
-            kernel32_lib_dir,
-            gcc_dir,
-        });
-    }
-
-    pub const FindNativeOptions = struct {
-        allocator: Allocator,
-        target: std.Target,
-
-        /// If enabled, will print human-friendly errors to stderr.
-        verbose: bool = false,
-    };
-
-    /// Finds the default, native libc.
-    pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation {
-        var self: LibCInstallation = .{};
-
-        if (is_darwin) {
-            if (!std.zig.system.darwin.isSdkInstalled(args.allocator))
-                return error.DarwinSdkNotFound;
-            const sdk = std.zig.system.darwin.getSdk(args.allocator, args.target) orelse
-                return error.DarwinSdkNotFound;
-            defer args.allocator.free(sdk);
-
-            self.include_dir = try fs.path.join(args.allocator, &.{
-                sdk, "usr/include",
-            });
-            self.sys_include_dir = try fs.path.join(args.allocator, &.{
-                sdk, "usr/include",
-            });
-            return self;
-        } else if (is_windows) {
-            var sdk: ZigWindowsSDK = ZigWindowsSDK.find(args.allocator) catch |err| switch (err) {
-                error.NotFound => return error.WindowsSdkNotFound,
-                error.PathTooLong => return error.WindowsSdkNotFound,
-                error.OutOfMemory => return error.OutOfMemory,
-            };
-            defer sdk.free(args.allocator);
-
-            try self.findNativeMsvcIncludeDir(args, &sdk);
-            try self.findNativeMsvcLibDir(args, &sdk);
-            try self.findNativeKernel32LibDir(args, &sdk);
-            try self.findNativeIncludeDirWindows(args, &sdk);
-            try self.findNativeCrtDirWindows(args, &sdk);
-        } else if (is_haiku) {
-            try self.findNativeIncludeDirPosix(args);
-            try self.findNativeCrtBeginDirHaiku(args);
-            self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib");
-        } else if (builtin.target.os.tag.isSolarish()) {
-            // There is only one libc, and its headers/libraries are always in the same spot.
-            self.include_dir = try args.allocator.dupeZ(u8, "/usr/include");
-            self.sys_include_dir = try args.allocator.dupeZ(u8, "/usr/include");
-            self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib/64");
-        } else if (std.process.can_spawn) {
-            try self.findNativeIncludeDirPosix(args);
-            switch (builtin.target.os.tag) {
-                .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib"),
-                .linux => try self.findNativeCrtDirPosix(args),
-                else => {},
-            }
-        } else {
-            return error.LibCRuntimeNotFound;
-        }
-        return self;
-    }
-
-    /// Must be the same allocator passed to `parse` or `findNative`.
-    pub fn deinit(self: *LibCInstallation, allocator: Allocator) void {
-        const fields = std.meta.fields(LibCInstallation);
-        inline for (fields) |field| {
-            if (@field(self, field.name)) |payload| {
-                allocator.free(payload);
-            }
-        }
-        self.* = undefined;
-    }
-
-    fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
-        const allocator = args.allocator;
-
-        // Detect infinite loops.
-        var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
-            error.Unexpected => unreachable, // WASI-only
-            else => |e| return e,
-        };
-        defer env_map.deinit();
-        const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: {
-            if (std.mem.eql(u8, phase, "1")) {
-                try env_map.put(inf_loop_env_key, "2");
-                break :blk true;
-            } else {
-                return error.ZigIsTheCCompiler;
-            }
-        } else blk: {
-            try env_map.put(inf_loop_env_key, "1");
-            break :blk false;
-        };
-
-        const dev_null = if (is_windows) "nul" else "/dev/null";
-
-        var argv = std.ArrayList([]const u8).init(allocator);
-        defer argv.deinit();
-
-        try appendCcExe(&argv, skip_cc_env_var);
-        try argv.appendSlice(&.{
-            "-E",
-            "-Wp,-v",
-            "-xc",
-            dev_null,
-        });
-
-        const run_res = std.ChildProcess.run(.{
-            .allocator = allocator,
-            .argv = argv.items,
-            .max_output_bytes = 1024 * 1024,
-            .env_map = &env_map,
-            // Some C compilers, such as Clang, are known to rely on argv[0] to find the path
-            // to their own executable, without even bothering to resolve PATH. This results in the message:
-            // error: unable to execute command: Executable "" doesn't exist!
-            // So we use the expandArg0 variant of ChildProcess to give them a helping hand.
-            .expand_arg0 = .expand,
-        }) catch |err| switch (err) {
-            error.OutOfMemory => return error.OutOfMemory,
-            else => {
-                printVerboseInvocation(argv.items, null, args.verbose, null);
-                return error.UnableToSpawnCCompiler;
-            },
-        };
-        defer {
-            allocator.free(run_res.stdout);
-            allocator.free(run_res.stderr);
-        }
-        switch (run_res.term) {
-            .Exited => |code| if (code != 0) {
-                printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr);
-                return error.CCompilerExitCode;
-            },
-            else => {
-                printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr);
-                return error.CCompilerCrashed;
-            },
-        }
-
-        var it = std.mem.tokenizeAny(u8, run_res.stderr, "\n\r");
-        var search_paths = std.ArrayList([]const u8).init(allocator);
-        defer search_paths.deinit();
-        while (it.next()) |line| {
-            if (line.len != 0 and line[0] == ' ') {
-                try search_paths.append(line);
-            }
-        }
-        if (search_paths.items.len == 0) {
-            return error.CCompilerCannotFindHeaders;
-        }
-
-        const include_dir_example_file = if (is_haiku) "posix/stdlib.h" else "stdlib.h";
-        const sys_include_dir_example_file = if (is_windows)
-            "sys\\types.h"
-        else if (is_haiku)
-            "errno.h"
-        else
-            "sys/errno.h";
-
-        var path_i: usize = 0;
-        while (path_i < search_paths.items.len) : (path_i += 1) {
-            // search in reverse order
-            const search_path_untrimmed = search_paths.items[search_paths.items.len - path_i - 1];
-            const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
-            var search_dir = fs.cwd().openDir(search_path, .{}) catch |err| switch (err) {
-                error.FileNotFound,
-                error.NotDir,
-                error.NoDevice,
-                => continue,
-
-                else => return error.FileSystem,
-            };
-            defer search_dir.close();
-
-            if (self.include_dir == null) {
-                if (search_dir.accessZ(include_dir_example_file, .{})) |_| {
-                    self.include_dir = try allocator.dupeZ(u8, search_path);
-                } else |err| switch (err) {
-                    error.FileNotFound => {},
-                    else => return error.FileSystem,
-                }
-            }
-
-            if (self.sys_include_dir == null) {
-                if (search_dir.accessZ(sys_include_dir_example_file, .{})) |_| {
-                    self.sys_include_dir = try allocator.dupeZ(u8, search_path);
-                } else |err| switch (err) {
-                    error.FileNotFound => {},
-                    else => return error.FileSystem,
-                }
-            }
-
-            if (self.include_dir != null and self.sys_include_dir != null) {
-                // Success.
-                return;
-            }
-        }
-
-        return error.LibCStdLibHeaderNotFound;
-    }
-
-    fn findNativeIncludeDirWindows(
-        self: *LibCInstallation,
-        args: FindNativeOptions,
-        sdk: *ZigWindowsSDK,
-    ) FindError!void {
-        const allocator = args.allocator;
-
-        var search_buf: [2]Search = undefined;
-        const searches = fillSearch(&search_buf, sdk);
-
-        var result_buf = std.ArrayList(u8).init(allocator);
-        defer result_buf.deinit();
-
-        for (searches) |search| {
-            result_buf.shrinkAndFree(0);
-            try result_buf.writer().print("{s}\\Include\\{s}\\ucrt", .{ search.path, search.version });
-
-            var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) {
-                error.FileNotFound,
-                error.NotDir,
-                error.NoDevice,
-                => continue,
-
-                else => return error.FileSystem,
-            };
-            defer dir.close();
-
-            dir.accessZ("stdlib.h", .{}) catch |err| switch (err) {
-                error.FileNotFound => continue,
-                else => return error.FileSystem,
-            };
-
-            self.include_dir = try result_buf.toOwnedSlice();
-            return;
-        }
-
-        return error.LibCStdLibHeaderNotFound;
-    }
-
-    fn findNativeCrtDirWindows(
-        self: *LibCInstallation,
-        args: FindNativeOptions,
-        sdk: *ZigWindowsSDK,
-    ) FindError!void {
-        const allocator = args.allocator;
-
-        var search_buf: [2]Search = undefined;
-        const searches = fillSearch(&search_buf, sdk);
-
-        var result_buf = std.ArrayList(u8).init(allocator);
-        defer result_buf.deinit();
-
-        const arch_sub_dir = switch (builtin.target.cpu.arch) {
-            .x86 => "x86",
-            .x86_64 => "x64",
-            .arm, .armeb => "arm",
-            .aarch64 => "arm64",
-            else => return error.UnsupportedArchitecture,
-        };
-
-        for (searches) |search| {
-            result_buf.shrinkAndFree(0);
-            try result_buf.writer().print("{s}\\Lib\\{s}\\ucrt\\{s}", .{ search.path, search.version, arch_sub_dir });
-
-            var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) {
-                error.FileNotFound,
-                error.NotDir,
-                error.NoDevice,
-                => continue,
-
-                else => return error.FileSystem,
-            };
-            defer dir.close();
-
-            dir.accessZ("ucrt.lib", .{}) catch |err| switch (err) {
-                error.FileNotFound => continue,
-                else => return error.FileSystem,
-            };
-
-            self.crt_dir = try result_buf.toOwnedSlice();
-            return;
-        }
-        return error.LibCRuntimeNotFound;
-    }
-
-    fn findNativeCrtDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
-        self.crt_dir = try ccPrintFileName(.{
-            .allocator = args.allocator,
-            .search_basename = "crt1.o",
-            .want_dirname = .only_dir,
-            .verbose = args.verbose,
-        });
-    }
-
-    fn findNativeCrtBeginDirHaiku(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
-        self.gcc_dir = try ccPrintFileName(.{
-            .allocator = args.allocator,
-            .search_basename = "crtbeginS.o",
-            .want_dirname = .only_dir,
-            .verbose = args.verbose,
-        });
-    }
-
-    fn findNativeKernel32LibDir(
-        self: *LibCInstallation,
-        args: FindNativeOptions,
-        sdk: *ZigWindowsSDK,
-    ) FindError!void {
-        const allocator = args.allocator;
-
-        var search_buf: [2]Search = undefined;
-        const searches = fillSearch(&search_buf, sdk);
-
-        var result_buf = std.ArrayList(u8).init(allocator);
-        defer result_buf.deinit();
-
-        const arch_sub_dir = switch (builtin.target.cpu.arch) {
-            .x86 => "x86",
-            .x86_64 => "x64",
-            .arm, .armeb => "arm",
-            .aarch64 => "arm64",
-            else => return error.UnsupportedArchitecture,
-        };
-
-        for (searches) |search| {
-            result_buf.shrinkAndFree(0);
-            const stream = result_buf.writer();
-            try stream.print("{s}\\Lib\\{s}\\um\\{s}", .{ search.path, search.version, arch_sub_dir });
-
-            var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) {
-                error.FileNotFound,
-                error.NotDir,
-                error.NoDevice,
-                => continue,
-
-                else => return error.FileSystem,
-            };
-            defer dir.close();
-
-            dir.accessZ("kernel32.lib", .{}) catch |err| switch (err) {
-                error.FileNotFound => continue,
-                else => return error.FileSystem,
-            };
-
-            self.kernel32_lib_dir = try result_buf.toOwnedSlice();
-            return;
-        }
-        return error.LibCKernel32LibNotFound;
-    }
-
-    fn findNativeMsvcIncludeDir(
-        self: *LibCInstallation,
-        args: FindNativeOptions,
-        sdk: *ZigWindowsSDK,
-    ) FindError!void {
-        const allocator = args.allocator;
-
-        const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCStdLibHeaderNotFound;
-        const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound;
-        const up2 = fs.path.dirname(up1) orelse return error.LibCStdLibHeaderNotFound;
-
-        const dir_path = try fs.path.join(allocator, &[_][]const u8{ up2, "include" });
-        errdefer allocator.free(dir_path);
-
-        var dir = fs.cwd().openDir(dir_path, .{}) catch |err| switch (err) {
-            error.FileNotFound,
-            error.NotDir,
-            error.NoDevice,
-            => return error.LibCStdLibHeaderNotFound,
-
-            else => return error.FileSystem,
-        };
-        defer dir.close();
-
-        dir.accessZ("vcruntime.h", .{}) catch |err| switch (err) {
-            error.FileNotFound => return error.LibCStdLibHeaderNotFound,
-            else => return error.FileSystem,
-        };
-
-        self.sys_include_dir = dir_path;
-    }
-
-    fn findNativeMsvcLibDir(
-        self: *LibCInstallation,
-        args: FindNativeOptions,
-        sdk: *ZigWindowsSDK,
-    ) FindError!void {
-        const allocator = args.allocator;
-        const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCRuntimeNotFound;
-        self.msvc_lib_dir = try allocator.dupe(u8, msvc_lib_dir);
-    }
-};
-
-pub const CCPrintFileNameOptions = struct {
-    allocator: Allocator,
-    search_basename: []const u8,
-    want_dirname: enum { full_path, only_dir },
-    verbose: bool = false,
-};
-
-/// caller owns returned memory
-fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 {
-    const allocator = args.allocator;
-
-    // Detect infinite loops.
-    var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
-        error.Unexpected => unreachable, // WASI-only
-        else => |e| return e,
-    };
-    defer env_map.deinit();
-    const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: {
-        if (std.mem.eql(u8, phase, "1")) {
-            try env_map.put(inf_loop_env_key, "2");
-            break :blk true;
-        } else {
-            return error.ZigIsTheCCompiler;
-        }
-    } else blk: {
-        try env_map.put(inf_loop_env_key, "1");
-        break :blk false;
-    };
-
-    var argv = std.ArrayList([]const u8).init(allocator);
-    defer argv.deinit();
-
-    const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={s}", .{args.search_basename});
-    defer allocator.free(arg1);
-
-    try appendCcExe(&argv, skip_cc_env_var);
-    try argv.append(arg1);
-
-    const run_res = std.ChildProcess.run(.{
-        .allocator = allocator,
-        .argv = argv.items,
-        .max_output_bytes = 1024 * 1024,
-        .env_map = &env_map,
-        // Some C compilers, such as Clang, are known to rely on argv[0] to find the path
-        // to their own executable, without even bothering to resolve PATH. This results in the message:
-        // error: unable to execute command: Executable "" doesn't exist!
-        // So we use the expandArg0 variant of ChildProcess to give them a helping hand.
-        .expand_arg0 = .expand,
-    }) catch |err| switch (err) {
-        error.OutOfMemory => return error.OutOfMemory,
-        else => return error.UnableToSpawnCCompiler,
-    };
-    defer {
-        allocator.free(run_res.stdout);
-        allocator.free(run_res.stderr);
-    }
-    switch (run_res.term) {
-        .Exited => |code| if (code != 0) {
-            printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr);
-            return error.CCompilerExitCode;
-        },
-        else => {
-            printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr);
-            return error.CCompilerCrashed;
-        },
-    }
-
-    var it = std.mem.tokenizeAny(u8, run_res.stdout, "\n\r");
-    const line = it.next() orelse return error.LibCRuntimeNotFound;
-    // When this command fails, it returns exit code 0 and duplicates the input file name.
-    // So we detect failure by checking if the output matches exactly the input.
-    if (std.mem.eql(u8, line, args.search_basename)) return error.LibCRuntimeNotFound;
-    switch (args.want_dirname) {
-        .full_path => return allocator.dupeZ(u8, line),
-        .only_dir => {
-            const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
-            return allocator.dupeZ(u8, dirname);
-        },
-    }
-}
-
-fn printVerboseInvocation(
-    argv: []const []const u8,
-    search_basename: ?[]const u8,
-    verbose: bool,
-    stderr: ?[]const u8,
-) void {
-    if (!verbose) return;
-
-    if (search_basename) |s| {
-        std.debug.print("Zig attempted to find the file '{s}' by executing this command:\n", .{s});
-    } else {
-        std.debug.print("Zig attempted to find the path to native system libc headers by executing this command:\n", .{});
-    }
-    for (argv, 0..) |arg, i| {
-        if (i != 0) std.debug.print(" ", .{});
-        std.debug.print("{s}", .{arg});
-    }
-    std.debug.print("\n", .{});
-    if (stderr) |s| {
-        std.debug.print("Output:\n==========\n{s}\n==========\n", .{s});
-    }
-}
-
-const Search = struct {
-    path: []const u8,
-    version: []const u8,
-};
-
-fn fillSearch(search_buf: *[2]Search, sdk: *ZigWindowsSDK) []Search {
-    var search_end: usize = 0;
-    if (sdk.windows10sdk) |windows10sdk| {
-        search_buf[search_end] = .{
-            .path = windows10sdk.path,
-            .version = windows10sdk.version,
-        };
-        search_end += 1;
-    }
-    if (sdk.windows81sdk) |windows81sdk| {
-        search_buf[search_end] = .{
-            .path = windows81sdk.path,
-            .version = windows81sdk.version,
-        };
-        search_end += 1;
-    }
-    return search_buf[0..search_end];
-}
-
-const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS";
-
-fn appendCcExe(args: *std.ArrayList([]const u8), skip_cc_env_var: bool) !void {
-    const default_cc_exe = if (is_windows) "cc.exe" else "cc";
-    try args.ensureUnusedCapacity(1);
-    if (skip_cc_env_var) {
-        args.appendAssumeCapacity(default_cc_exe);
-        return;
-    }
-    const cc_env_var = EnvVar.CC.getPosix() orelse {
-        args.appendAssumeCapacity(default_cc_exe);
-        return;
-    };
-    // Respect space-separated flags to the C compiler.
-    var it = std.mem.tokenizeScalar(u8, cc_env_var, ' ');
-    while (it.next()) |arg| {
-        try args.append(arg);
-    }
-}
src/link.zig
@@ -12,7 +12,7 @@ const Air = @import("Air.zig");
 const Allocator = std.mem.Allocator;
 const Cache = std.Build.Cache;
 const Compilation = @import("Compilation.zig");
-const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
+const LibCInstallation = std.zig.LibCInstallation;
 const Liveness = @import("Liveness.zig");
 const Module = @import("Module.zig");
 const InternPool = @import("InternPool.zig");
src/main.zig
@@ -19,8 +19,8 @@ const link = @import("link.zig");
 const Package = @import("Package.zig");
 const build_options = @import("build_options");
 const introspect = @import("introspect.zig");
-const EnvVar = introspect.EnvVar;
-const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
+const EnvVar = std.zig.EnvVar;
+const LibCInstallation = std.zig.LibCInstallation;
 const wasi_libc = @import("wasi_libc.zig");
 const Cache = std.Build.Cache;
 const target_util = @import("target.zig");
@@ -294,17 +294,17 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
     } else if (mem.eql(u8, cmd, "rc")) {
         return cmdRc(gpa, arena, args[1..]);
     } else if (mem.eql(u8, cmd, "fmt")) {
-        return jitCmd(gpa, arena, cmd_args, "fmt", "fmt.zig");
+        return jitCmd(gpa, arena, cmd_args, "fmt", "fmt.zig", false);
     } else if (mem.eql(u8, cmd, "objcopy")) {
         return @import("objcopy.zig").cmdObjCopy(gpa, arena, cmd_args);
     } else if (mem.eql(u8, cmd, "fetch")) {
         return cmdFetch(gpa, arena, cmd_args);
     } else if (mem.eql(u8, cmd, "libc")) {
-        return cmdLibC(gpa, cmd_args);
+        return jitCmd(gpa, arena, cmd_args, "libc", "libc.zig", true);
     } else if (mem.eql(u8, cmd, "init")) {
         return cmdInit(gpa, arena, cmd_args);
     } else if (mem.eql(u8, cmd, "targets")) {
-        const host = resolveTargetQueryOrFatal(.{});
+        const host = std.zig.resolveTargetQueryOrFatal(.{});
         const stdout = io.getStdOut().writer();
         return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, host);
     } else if (mem.eql(u8, cmd, "version")) {
@@ -317,7 +317,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
         verifyLibcxxCorrectlyLinked();
         return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer());
     } else if (mem.eql(u8, cmd, "reduce")) {
-        return jitCmd(gpa, arena, cmd_args, "reduce", "reduce.zig");
+        return jitCmd(gpa, arena, cmd_args, "reduce", "reduce.zig", false);
     } else if (mem.eql(u8, cmd, "zen")) {
         return io.getStdOut().writeAll(info_zen);
     } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) {
@@ -3259,7 +3259,7 @@ fn buildOutputType(
             const triple_name = try target.zigTriple(arena);
             std.log.err("unable to find or provide libc for target '{s}'", .{triple_name});
 
-            for (target_util.available_libcs) |t| {
+            for (std.zig.target.available_libcs) |t| {
                 if (t.arch == target.cpu.arch and t.os == target.os.tag) {
                     if (t.os_ver) |os_ver| {
                         std.log.info("zig can provide libc for related target {s}-{s}.{d}-{s}", .{
@@ -3530,16 +3530,16 @@ fn createModule(
             }
         }
 
-        const target_query = parseTargetQueryOrReportFatalError(arena, target_parse_options);
+        const target_query = std.zig.parseTargetQueryOrReportFatalError(arena, target_parse_options);
         const adjusted_target_query = a: {
             if (!target_query.isNative()) break :a target_query;
             if (create_module.host_triple) |triple| target_parse_options.arch_os_abi = triple;
             if (create_module.host_cpu) |cpu| target_parse_options.cpu_features = cpu;
             if (create_module.host_dynamic_linker) |dl| target_parse_options.dynamic_linker = dl;
-            break :a parseTargetQueryOrReportFatalError(arena, target_parse_options);
+            break :a std.zig.parseTargetQueryOrReportFatalError(arena, target_parse_options);
         };
 
-        const target = resolveTargetQueryOrFatal(adjusted_target_query);
+        const target = std.zig.resolveTargetQueryOrFatal(adjusted_target_query);
         break :t .{
             .result = target,
             .is_native_os = target_query.isNativeOs(),
@@ -4210,59 +4210,6 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void {
     }
 }
 
-fn parseTargetQueryOrReportFatalError(
-    allocator: Allocator,
-    opts: std.Target.Query.ParseOptions,
-) std.Target.Query {
-    var opts_with_diags = opts;
-    var diags: std.Target.Query.ParseOptions.Diagnostics = .{};
-    if (opts_with_diags.diagnostics == null) {
-        opts_with_diags.diagnostics = &diags;
-    }
-    return std.Target.Query.parse(opts_with_diags) catch |err| switch (err) {
-        error.UnknownCpuModel => {
-            help: {
-                var help_text = std.ArrayList(u8).init(allocator);
-                defer help_text.deinit();
-                for (diags.arch.?.allCpuModels()) |cpu| {
-                    help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help;
-                }
-                std.log.info("available CPUs for architecture '{s}':\n{s}", .{
-                    @tagName(diags.arch.?), help_text.items,
-                });
-            }
-            fatal("unknown CPU: '{s}'", .{diags.cpu_name.?});
-        },
-        error.UnknownCpuFeature => {
-            help: {
-                var help_text = std.ArrayList(u8).init(allocator);
-                defer help_text.deinit();
-                for (diags.arch.?.allFeaturesList()) |feature| {
-                    help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help;
-                }
-                std.log.info("available CPU features for architecture '{s}':\n{s}", .{
-                    @tagName(diags.arch.?), help_text.items,
-                });
-            }
-            fatal("unknown CPU feature: '{s}'", .{diags.unknown_feature_name.?});
-        },
-        error.UnknownObjectFormat => {
-            help: {
-                var help_text = std.ArrayList(u8).init(allocator);
-                defer help_text.deinit();
-                inline for (@typeInfo(std.Target.ObjectFormat).Enum.fields) |field| {
-                    help_text.writer().print(" {s}\n", .{field.name}) catch break :help;
-                }
-                std.log.info("available object formats:\n{s}", .{help_text.items});
-            }
-            fatal("unknown object format: '{s}'", .{opts.object_format.?});
-        },
-        else => |e| fatal("unable to parse target query '{s}': {s}", .{
-            opts.arch_os_abi, @errorName(e),
-        }),
-    };
-}
-
 fn runOrTest(
     comp: *Compilation,
     gpa: Allocator,
@@ -4871,9 +4818,9 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes:
                     .os_tag = .windows,
                     .abi = .msvc,
                 };
-                const target = resolveTargetQueryOrFatal(target_query);
+                const target = std.zig.resolveTargetQueryOrFatal(target_query);
                 const is_native_abi = target_query.isNativeAbi();
-                const detected_libc = Compilation.detectLibCIncludeDirs(arena, zig_lib_dir, target, is_native_abi, true, null) catch |err| {
+                const detected_libc = std.zig.LibCDirs.detect(arena, zig_lib_dir, target, is_native_abi, true, null) catch |err| {
                     if (cur_includes == .any) {
                         // fall back to mingw
                         cur_includes = .gnu;
@@ -4899,9 +4846,9 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes:
                     .os_tag = .windows,
                     .abi = .gnu,
                 };
-                const target = resolveTargetQueryOrFatal(target_query);
+                const target = std.zig.resolveTargetQueryOrFatal(target_query);
                 const is_native_abi = target_query.isNativeAbi();
-                const detected_libc = try Compilation.detectLibCIncludeDirs(arena, zig_lib_dir, target, is_native_abi, true, null);
+                const detected_libc = try std.zig.LibCDirs.detect(arena, zig_lib_dir, target, is_native_abi, true, null);
                 return .{
                     .include_paths = detected_libc.libc_include_dir_list,
                     .target_abi = "gnu",
@@ -4912,136 +4859,6 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes:
     }
 }
 
-const usage_libc =
-    \\Usage: zig libc
-    \\
-    \\    Detect the native libc installation and print the resulting
-    \\    paths to stdout. You can save this into a file and then edit
-    \\    the paths to create a cross compilation libc kit. Then you
-    \\    can pass `--libc [file]` for Zig to use it.
-    \\
-    \\Usage: zig libc [paths_file]
-    \\
-    \\    Parse a libc installation text file and validate it.
-    \\
-    \\Options:
-    \\  -h, --help             Print this help and exit
-    \\  -target [name]         <arch><sub>-<os>-<abi> see the targets command
-    \\  -includes              Print the libc include directories for the target
-    \\
-;
-
-fn cmdLibC(gpa: Allocator, args: []const []const u8) !void {
-    var input_file: ?[]const u8 = null;
-    var target_arch_os_abi: []const u8 = "native";
-    var print_includes: bool = false;
-    {
-        var i: usize = 0;
-        while (i < args.len) : (i += 1) {
-            const arg = args[i];
-            if (mem.startsWith(u8, arg, "-")) {
-                if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
-                    const stdout = io.getStdOut().writer();
-                    try stdout.writeAll(usage_libc);
-                    return cleanExit();
-                } else if (mem.eql(u8, arg, "-target")) {
-                    if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
-                    i += 1;
-                    target_arch_os_abi = args[i];
-                } else if (mem.eql(u8, arg, "-includes")) {
-                    print_includes = true;
-                } else {
-                    fatal("unrecognized parameter: '{s}'", .{arg});
-                }
-            } else if (input_file != null) {
-                fatal("unexpected extra parameter: '{s}'", .{arg});
-            } else {
-                input_file = arg;
-            }
-        }
-    }
-
-    const target_query = parseTargetQueryOrReportFatalError(gpa, .{
-        .arch_os_abi = target_arch_os_abi,
-    });
-    const target = resolveTargetQueryOrFatal(target_query);
-
-    if (print_includes) {
-        var arena_state = std.heap.ArenaAllocator.init(gpa);
-        defer arena_state.deinit();
-        const arena = arena_state.allocator();
-
-        const libc_installation: ?*LibCInstallation = libc: {
-            if (input_file) |libc_file| {
-                const libc = try arena.create(LibCInstallation);
-                libc.* = LibCInstallation.parse(arena, libc_file, target) catch |err| {
-                    fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) });
-                };
-                break :libc libc;
-            } else {
-                break :libc null;
-            }
-        };
-
-        const self_exe_path = try introspect.findZigExePath(arena);
-        var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
-            fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)});
-        };
-        defer zig_lib_directory.handle.close();
-
-        const is_native_abi = target_query.isNativeAbi();
-
-        const libc_dirs = Compilation.detectLibCIncludeDirs(
-            arena,
-            zig_lib_directory.path.?,
-            target,
-            is_native_abi,
-            true,
-            libc_installation,
-        ) catch |err| {
-            const zig_target = try target.zigTriple(arena);
-            fatal("unable to detect libc for target {s}: {s}", .{ zig_target, @errorName(err) });
-        };
-
-        if (libc_dirs.libc_include_dir_list.len == 0) {
-            const zig_target = try target.zigTriple(arena);
-            fatal("no include dirs detected for target {s}", .{zig_target});
-        }
-
-        var bw = io.bufferedWriter(io.getStdOut().writer());
-        var writer = bw.writer();
-        for (libc_dirs.libc_include_dir_list) |include_dir| {
-            try writer.writeAll(include_dir);
-            try writer.writeByte('\n');
-        }
-        try bw.flush();
-        return cleanExit();
-    }
-
-    if (input_file) |libc_file| {
-        var libc = LibCInstallation.parse(gpa, libc_file, target) catch |err| {
-            fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) });
-        };
-        defer libc.deinit(gpa);
-    } else {
-        if (!target_query.isNative()) {
-            fatal("unable to detect libc for non-native target", .{});
-        }
-        var libc = LibCInstallation.findNative(.{
-            .allocator = gpa,
-            .verbose = true,
-            .target = target,
-        }) catch |err| {
-            fatal("unable to detect native libc: {s}", .{@errorName(err)});
-        };
-        defer libc.deinit(gpa);
-
-        var bw = io.bufferedWriter(io.getStdOut().writer());
-        try libc.render(bw.writer());
-        try bw.flush();
-    }
-}
-
 const usage_init =
     \\Usage: zig init
     \\
@@ -5293,7 +5110,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
 
     const target_query: std.Target.Query = .{};
     const resolved_target: Package.Module.ResolvedTarget = .{
-        .result = resolveTargetQueryOrFatal(target_query),
+        .result = std.zig.resolveTargetQueryOrFatal(target_query),
         .is_native_os = true,
         .is_native_abi = true,
     };
@@ -5711,12 +5528,13 @@ fn jitCmd(
     args: []const []const u8,
     cmd_name: []const u8,
     root_src_path: []const u8,
+    prepend_zig_lib_dir_path: bool,
 ) !void {
     const color: Color = .auto;
 
     const target_query: std.Target.Query = .{};
     const resolved_target: Package.Module.ResolvedTarget = .{
-        .result = resolveTargetQueryOrFatal(target_query),
+        .result = std.zig.resolveTargetQueryOrFatal(target_query),
         .is_native_os = true,
         .is_native_abi = true,
     };
@@ -5766,7 +5584,7 @@ fn jitCmd(
     defer thread_pool.deinit();
 
     var child_argv: std.ArrayListUnmanaged([]const u8) = .{};
-    try child_argv.ensureUnusedCapacity(arena, args.len + 1);
+    try child_argv.ensureUnusedCapacity(arena, args.len + 2);
 
     // We want to release all the locks before executing the child process, so we make a nice
     // big block here to ensure the cleanup gets run when we extract out our argv.
@@ -5829,6 +5647,9 @@ fn jitCmd(
         child_argv.appendAssumeCapacity(exe_path);
     }
 
+    if (prepend_zig_lib_dir_path)
+        child_argv.appendAssumeCapacity(zig_lib_directory.path.?);
+
     child_argv.appendSliceAssumeCapacity(args);
 
     if (process.can_execv) {
@@ -6703,7 +6524,7 @@ fn warnAboutForeignBinaries(
     link_libc: bool,
 ) !void {
     const host_query: std.Target.Query = .{};
-    const host_target = resolveTargetQueryOrFatal(host_query);
+    const host_target = std.zig.resolveTargetQueryOrFatal(host_query);
 
     switch (std.zig.system.getExternalExecutor(host_target, target, .{ .link_libc = link_libc })) {
         .native => return,
@@ -7559,11 +7380,6 @@ fn parseWasiExecModel(s: []const u8) std.builtin.WasiExecModel {
         fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{s});
 }
 
-fn resolveTargetQueryOrFatal(target_query: std.Target.Query) std.Target {
-    return std.zig.system.resolveTargetQuery(target_query) catch |err|
-        fatal("unable to resolve target: {s}", .{@errorName(err)});
-}
-
 fn parseStackSize(s: []const u8) u64 {
     return std.fmt.parseUnsigned(u64, s, 0) catch |err|
         fatal("unable to parse stack size '{s}': {s}", .{ s, @errorName(err) });
src/musl.zig
@@ -4,6 +4,7 @@ const mem = std.mem;
 const path = std.fs.path;
 const assert = std.debug.assert;
 const Module = @import("Package/Module.zig");
+const archName = std.zig.target.muslArchName;
 
 const Compilation = @import("Compilation.zig");
 const build_options = @import("build_options");
@@ -294,30 +295,6 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr
     }
 }
 
-fn archName(arch: std.Target.Cpu.Arch) [:0]const u8 {
-    switch (arch) {
-        .aarch64, .aarch64_be => return "aarch64",
-        .arm, .armeb, .thumb, .thumbeb => return "arm",
-        .x86 => return "i386",
-        .mips, .mipsel => return "mips",
-        .mips64el, .mips64 => return "mips64",
-        .powerpc => return "powerpc",
-        .powerpc64, .powerpc64le => return "powerpc64",
-        .riscv64 => return "riscv64",
-        .s390x => return "s390x",
-        .wasm32, .wasm64 => return "wasm",
-        .x86_64 => return "x86_64",
-        else => unreachable,
-    }
-}
-
-pub fn archNameHeaders(arch: std.Target.Cpu.Arch) [:0]const u8 {
-    return switch (arch) {
-        .x86 => return "x86",
-        else => archName(arch),
-    };
-}
-
 // Return true if musl has arch-specific crti/crtn sources.
 // See lib/libc/musl/crt/ARCH/crt?.s .
 pub fn needsCrtiCrtn(target: std.Target) bool {
@@ -405,7 +382,7 @@ fn addCcArgs(
     const arch_name = archName(target.cpu.arch);
     const os_name = @tagName(target.os.tag);
     const triple = try std.fmt.allocPrint(arena, "{s}-{s}-musl", .{
-        archNameHeaders(target.cpu.arch), os_name,
+        std.zig.target.muslArchNameHeaders(target.cpu.arch), os_name,
     });
     const o_arg = if (want_O3) "-O3" else "-Os";
 
src/print_env.zig
@@ -47,9 +47,9 @@ pub fn cmdEnv(arena: Allocator, args: []const []const u8, stdout: std.fs.File.Wr
 
     try jws.objectField("env");
     try jws.beginObject();
-    inline for (@typeInfo(introspect.EnvVar).Enum.fields) |field| {
+    inline for (@typeInfo(std.zig.EnvVar).Enum.fields) |field| {
         try jws.objectField(field.name);
-        try jws.write(try @field(introspect.EnvVar, field.name).get(arena));
+        try jws.write(try @field(std.zig.EnvVar, field.name).get(arena));
     }
     try jws.endObject();
 
src/print_targets.zig
@@ -67,7 +67,7 @@ pub fn cmdTargets(
 
     try jws.objectField("libc");
     try jws.beginArray();
-    for (target.available_libcs) |libc| {
+    for (std.zig.target.available_libcs) |libc| {
         const tmp = try std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{
             @tagName(libc.arch), @tagName(libc.os), @tagName(libc.abi),
         });
src/target.zig
@@ -6,169 +6,6 @@ const Feature = @import("Module.zig").Feature;
 
 pub const default_stack_protector_buffer_size = 4;
 
-pub const ArchOsAbi = struct {
-    arch: std.Target.Cpu.Arch,
-    os: std.Target.Os.Tag,
-    abi: std.Target.Abi,
-    os_ver: ?std.SemanticVersion = null,
-
-    // Minimum glibc version that provides support for the arch/os when ABI is GNU.
-    glibc_min: ?std.SemanticVersion = null,
-};
-
-pub const available_libcs = [_]ArchOsAbi{
-    .{ .arch = .aarch64_be, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 17, .patch = 0 } },
-    .{ .arch = .aarch64_be, .os = .linux, .abi = .musl },
-    .{ .arch = .aarch64_be, .os = .windows, .abi = .gnu },
-    .{ .arch = .aarch64, .os = .linux, .abi = .gnu },
-    .{ .arch = .aarch64, .os = .linux, .abi = .musl },
-    .{ .arch = .aarch64, .os = .windows, .abi = .gnu },
-    .{ .arch = .aarch64, .os = .macos, .abi = .none, .os_ver = .{ .major = 11, .minor = 0, .patch = 0 } },
-    .{ .arch = .armeb, .os = .linux, .abi = .gnueabi },
-    .{ .arch = .armeb, .os = .linux, .abi = .gnueabihf },
-    .{ .arch = .armeb, .os = .linux, .abi = .musleabi },
-    .{ .arch = .armeb, .os = .linux, .abi = .musleabihf },
-    .{ .arch = .armeb, .os = .windows, .abi = .gnu },
-    .{ .arch = .arm, .os = .linux, .abi = .gnueabi },
-    .{ .arch = .arm, .os = .linux, .abi = .gnueabihf },
-    .{ .arch = .arm, .os = .linux, .abi = .musleabi },
-    .{ .arch = .arm, .os = .linux, .abi = .musleabihf },
-    .{ .arch = .thumb, .os = .linux, .abi = .gnueabi },
-    .{ .arch = .thumb, .os = .linux, .abi = .gnueabihf },
-    .{ .arch = .thumb, .os = .linux, .abi = .musleabi },
-    .{ .arch = .thumb, .os = .linux, .abi = .musleabihf },
-    .{ .arch = .arm, .os = .windows, .abi = .gnu },
-    .{ .arch = .csky, .os = .linux, .abi = .gnueabi },
-    .{ .arch = .csky, .os = .linux, .abi = .gnueabihf },
-    .{ .arch = .x86, .os = .linux, .abi = .gnu },
-    .{ .arch = .x86, .os = .linux, .abi = .musl },
-    .{ .arch = .x86, .os = .windows, .abi = .gnu },
-    .{ .arch = .m68k, .os = .linux, .abi = .gnu },
-    .{ .arch = .m68k, .os = .linux, .abi = .musl },
-    .{ .arch = .mips64el, .os = .linux, .abi = .gnuabi64 },
-    .{ .arch = .mips64el, .os = .linux, .abi = .gnuabin32 },
-    .{ .arch = .mips64el, .os = .linux, .abi = .musl },
-    .{ .arch = .mips64, .os = .linux, .abi = .gnuabi64 },
-    .{ .arch = .mips64, .os = .linux, .abi = .gnuabin32 },
-    .{ .arch = .mips64, .os = .linux, .abi = .musl },
-    .{ .arch = .mipsel, .os = .linux, .abi = .gnueabi },
-    .{ .arch = .mipsel, .os = .linux, .abi = .gnueabihf },
-    .{ .arch = .mipsel, .os = .linux, .abi = .musl },
-    .{ .arch = .mips, .os = .linux, .abi = .gnueabi },
-    .{ .arch = .mips, .os = .linux, .abi = .gnueabihf },
-    .{ .arch = .mips, .os = .linux, .abi = .musl },
-    .{ .arch = .powerpc64le, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 19, .patch = 0 } },
-    .{ .arch = .powerpc64le, .os = .linux, .abi = .musl },
-    .{ .arch = .powerpc64, .os = .linux, .abi = .gnu },
-    .{ .arch = .powerpc64, .os = .linux, .abi = .musl },
-    .{ .arch = .powerpc, .os = .linux, .abi = .gnueabi },
-    .{ .arch = .powerpc, .os = .linux, .abi = .gnueabihf },
-    .{ .arch = .powerpc, .os = .linux, .abi = .musl },
-    .{ .arch = .riscv64, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 27, .patch = 0 } },
-    .{ .arch = .riscv64, .os = .linux, .abi = .musl },
-    .{ .arch = .s390x, .os = .linux, .abi = .gnu },
-    .{ .arch = .s390x, .os = .linux, .abi = .musl },
-    .{ .arch = .sparc, .os = .linux, .abi = .gnu },
-    .{ .arch = .sparc64, .os = .linux, .abi = .gnu },
-    .{ .arch = .wasm32, .os = .freestanding, .abi = .musl },
-    .{ .arch = .wasm32, .os = .wasi, .abi = .musl },
-    .{ .arch = .x86_64, .os = .linux, .abi = .gnu },
-    .{ .arch = .x86_64, .os = .linux, .abi = .gnux32 },
-    .{ .arch = .x86_64, .os = .linux, .abi = .musl },
-    .{ .arch = .x86_64, .os = .windows, .abi = .gnu },
-    .{ .arch = .x86_64, .os = .macos, .abi = .none, .os_ver = .{ .major = 10, .minor = 7, .patch = 0 } },
-};
-
-pub fn libCGenericName(target: std.Target) [:0]const u8 {
-    switch (target.os.tag) {
-        .windows => return "mingw",
-        .macos, .ios, .tvos, .watchos => return "darwin",
-        else => {},
-    }
-    switch (target.abi) {
-        .gnu,
-        .gnuabin32,
-        .gnuabi64,
-        .gnueabi,
-        .gnueabihf,
-        .gnuf32,
-        .gnuf64,
-        .gnusf,
-        .gnux32,
-        .gnuilp32,
-        => return "glibc",
-        .musl,
-        .musleabi,
-        .musleabihf,
-        .muslx32,
-        .none,
-        => return "musl",
-        .code16,
-        .eabi,
-        .eabihf,
-        .android,
-        .msvc,
-        .itanium,
-        .cygnus,
-        .coreclr,
-        .simulator,
-        .macabi,
-        => unreachable,
-
-        .pixel,
-        .vertex,
-        .geometry,
-        .hull,
-        .domain,
-        .compute,
-        .library,
-        .raygeneration,
-        .intersection,
-        .anyhit,
-        .closesthit,
-        .miss,
-        .callable,
-        .mesh,
-        .amplification,
-        => unreachable,
-    }
-}
-
-pub fn osArchName(target: std.Target) [:0]const u8 {
-    return switch (target.os.tag) {
-        .linux => switch (target.cpu.arch) {
-            .arm, .armeb, .thumb, .thumbeb => "arm",
-            .aarch64, .aarch64_be, .aarch64_32 => "aarch64",
-            .mips, .mipsel, .mips64, .mips64el => "mips",
-            .powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc",
-            .riscv32, .riscv64 => "riscv",
-            .sparc, .sparcel, .sparc64 => "sparc",
-            .x86, .x86_64 => "x86",
-            else => @tagName(target.cpu.arch),
-        },
-        else => @tagName(target.cpu.arch),
-    };
-}
-
-pub fn canBuildLibC(target: std.Target) bool {
-    for (available_libcs) |libc| {
-        if (target.cpu.arch == libc.arch and target.os.tag == libc.os and target.abi == libc.abi) {
-            if (target.os.tag == .macos) {
-                const ver = target.os.version_range.semver;
-                return ver.min.order(libc.os_ver.?) != .lt;
-            }
-            // Ensure glibc (aka *-linux-gnu) version is supported
-            if (target.isGnuLibC()) {
-                const min_glibc_ver = libc.glibc_min orelse return true;
-                const target_glibc_ver = target.os.version_range.linux.glibc;
-                return target_glibc_ver.order(min_glibc_ver) != .lt;
-            }
-            return true;
-        }
-    }
-    return false;
-}
-
 pub fn cannotDynamicLink(target: std.Target) bool {
     return switch (target.os.tag) {
         .freestanding, .other => true,
src/wasi_libc.zig
@@ -5,8 +5,6 @@ const path = std.fs.path;
 const Allocator = std.mem.Allocator;
 const Compilation = @import("Compilation.zig");
 const build_options = @import("build_options");
-const target_util = @import("target.zig");
-const musl = @import("musl.zig");
 
 pub const CRTFile = enum {
     crt1_reactor_o,
@@ -273,7 +271,7 @@ fn addCCArgs(
     options: CCOptions,
 ) error{OutOfMemory}!void {
     const target = comp.getTarget();
-    const arch_name = musl.archNameHeaders(target.cpu.arch);
+    const arch_name = std.zig.target.muslArchNameHeaders(target.cpu.arch);
     const os_name = @tagName(target.os.tag);
     const triple = try std.fmt.allocPrint(arena, "{s}-{s}-musl", .{ arch_name, os_name });
     const o_arg = if (options.want_O3) "-O3" else "-Os";
CMakeLists.txt
@@ -507,16 +507,18 @@ set(ZIG_STAGE2_SOURCES
     "${CMAKE_SOURCE_DIR}/lib/std/zig/Ast.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/zig/AstGen.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/zig/AstRlAnnotate.zig"
-    "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig"
+    "${CMAKE_SOURCE_DIR}/lib/std/zig/LibCInstallation.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig"
-    "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/zig/Server.zig"
+    "${CMAKE_SOURCE_DIR}/lib/std/zig/WindowsSdk.zig"
+    "${CMAKE_SOURCE_DIR}/lib/std/zig/Zir.zig"
+    "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig"
+    "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/zig/system/x86.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig"
-    "${CMAKE_SOURCE_DIR}/lib/std/zig/Zir.zig"
     "${CMAKE_SOURCE_DIR}/src/Air.zig"
     "${CMAKE_SOURCE_DIR}/src/Compilation.zig"
     "${CMAKE_SOURCE_DIR}/src/Compilation/Config.zig"
@@ -570,7 +572,6 @@ set(ZIG_STAGE2_SOURCES
     "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig"
     "${CMAKE_SOURCE_DIR}/src/glibc.zig"
     "${CMAKE_SOURCE_DIR}/src/introspect.zig"
-    "${CMAKE_SOURCE_DIR}/src/libc_installation.zig"
     "${CMAKE_SOURCE_DIR}/src/libcxx.zig"
     "${CMAKE_SOURCE_DIR}/src/libtsan.zig"
     "${CMAKE_SOURCE_DIR}/src/libunwind.zig"
@@ -645,7 +646,6 @@ set(ZIG_STAGE2_SOURCES
     "${CMAKE_SOURCE_DIR}/src/translate_c/ast.zig"
     "${CMAKE_SOURCE_DIR}/src/type.zig"
     "${CMAKE_SOURCE_DIR}/src/wasi_libc.zig"
-    "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig"
     "${CMAKE_SOURCE_DIR}/src/stubs/aro_builtins.zig"
     "${CMAKE_SOURCE_DIR}/src/stubs/aro_names.zig"
 )