commit a4380a30f5aeafd03e909164b28550e5960500f9 (tree)
parent 6e078883eebd0532928dba0c70e86a2eee0f246b
Author: Andrew Kelley <andrew@ziglang.org>
Date: Tue, 27 Feb 2024 17:12:53 -0700
move `zig libc` command to be lazily built
part of #19063
This is a prerequisite for doing the same for Resinator.
Diffstat:
21 files changed, 2395 insertions(+), 2377 deletions(-)
diff --git a/CMakeLists.txt b/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"
)
diff --git a/lib/compiler/libc.zig b/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);
+}
diff --git a/lib/std/Target.zig b/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");
diff --git a/lib/std/zig.zig b/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;
}
diff --git a/lib/std/zig/LibCDirs.zig b/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;
diff --git a/lib/std/zig/LibCInstallation.zig b/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);
diff --git a/lib/std/zig/WindowsSdk.zig b/lib/std/zig/WindowsSdk.zig
@@ -0,0 +1,964 @@
+windows10sdk: ?Windows10Sdk,
+windows81sdk: ?Windows81Sdk,
+msvc_lib_dir: ?[]const u8,
+
+const WindowsSdk = @This();
+const std = @import("std");
+const builtin = @import("builtin");
+
+const windows = std.os.windows;
+const RRF = windows.advapi32.RRF;
+
+const WINDOWS_KIT_REG_KEY = "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots";
+
+// https://learn.microsoft.com/en-us/windows/win32/msi/productversion
+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.
+fn iterateAndFilterBySemVer(
+ iterator: *std.fs.Dir.Iterator,
+ allocator: std.mem.Allocator,
+ comptime optional_prefix: ?[]const u8,
+) error{ OutOfMemory, VersionNotFound }![][]const u8 {
+ var dirs_filtered_list = std.ArrayList([]const u8).init(allocator);
+ errdefer {
+ for (dirs_filtered_list.items) |filtered_dir| allocator.free(filtered_dir);
+ dirs_filtered_list.deinit();
+ }
+
+ var normalized_name_buf: [std.fs.MAX_NAME_BYTES + ".0+build.0".len]u8 = undefined;
+ var normalized_name_fbs = std.io.fixedBufferStream(&normalized_name_buf);
+ const normalized_name_w = normalized_name_fbs.writer();
+ iterate_folder: while (true) : (normalized_name_fbs.reset()) {
+ const maybe_entry = iterator.next() catch continue :iterate_folder;
+ const entry = maybe_entry orelse break :iterate_folder;
+
+ if (entry.kind != .directory)
+ continue :iterate_folder;
+
+ // invalidated on next iteration
+ const subfolder_name = blk: {
+ if (comptime optional_prefix) |prefix| {
+ if (!std.mem.startsWith(u8, entry.name, prefix)) continue :iterate_folder;
+ break :blk entry.name[prefix.len..];
+ } else break :blk entry.name;
+ };
+
+ { // check if subfolder name looks similar to SemVer
+ switch (std.mem.count(u8, subfolder_name, ".")) {
+ 0 => normalized_name_w.print("{s}.0.0+build.0", .{subfolder_name}) catch unreachable, // 17 => 17.0.0+build.0
+ 1 => if (std.mem.indexOfScalar(u8, subfolder_name, '_')) |underscore_pos| blk: { // 17.0_9e9cbb98 => 17.0.1+build.9e9cbb98
+ var subfolder_name_tmp_copy_buf: [std.fs.MAX_NAME_BYTES]u8 = undefined;
+ const subfolder_name_tmp_copy = subfolder_name_tmp_copy_buf[0..subfolder_name.len];
+ @memcpy(subfolder_name_tmp_copy, subfolder_name);
+
+ subfolder_name_tmp_copy[underscore_pos] = '.'; // 17.0_9e9cbb98 => 17.0.9e9cbb98
+ var subfolder_name_parts = std.mem.splitScalar(u8, subfolder_name_tmp_copy, '.'); // [ 17, 0, 9e9cbb98 ]
+
+ const first = subfolder_name_parts.first(); // 17
+ const second = subfolder_name_parts.next().?; // 0
+ const third = subfolder_name_parts.rest(); // 9e9cbb98
+
+ break :blk normalized_name_w.print("{s}.{s}.1+build.{s}", .{ first, second, third }) catch unreachable; // [ 17, 0, 9e9cbb98 ] => 17.0.1+build.9e9cbb98
+ } else normalized_name_w.print("{s}.0+build.0", .{subfolder_name}) catch unreachable, // 17.0 => 17.0.0+build.0
+ else => normalized_name_w.print("{s}+build.0", .{subfolder_name}) catch unreachable, // 17.0.0 => 17.0.0+build.0
+ }
+ const subfolder_name_normalized: []const u8 = normalized_name_fbs.getWritten();
+ const sem_ver = std.SemanticVersion.parse(subfolder_name_normalized);
+ _ = sem_ver catch continue :iterate_folder;
+ }
+ // entry.name passed check
+
+ const subfolder_name_allocated = try allocator.dupe(u8, subfolder_name);
+ errdefer allocator.free(subfolder_name_allocated);
+ try dirs_filtered_list.append(subfolder_name_allocated);
+ }
+
+ const dirs_filtered_slice = try dirs_filtered_list.toOwnedSlice();
+ // Keep in mind that order of these names is not guaranteed by Windows,
+ // so we cannot just reverse or "while (popOrNull())" this ArrayList.
+ std.mem.sortUnstable([]const u8, dirs_filtered_slice, {}, struct {
+ fn desc(_: void, lhs: []const u8, rhs: []const u8) bool {
+ return std.mem.order(u8, lhs, rhs) == .gt;
+ }
+ }.desc);
+ return dirs_filtered_slice;
+}
+
+const RegistryWtf8 = struct {
+ key: windows.HKEY,
+
+ /// Assert that `key` is valid WTF-8 string
+ pub fn openKey(hkey: windows.HKEY, key: []const u8) error{KeyNotFound}!RegistryWtf8 {
+ const key_wtf16le: [:0]const u16 = key_wtf16le: {
+ var key_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined;
+ const key_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(key_wtf16le_buf[0..], key) catch |err| switch (err) {
+ error.InvalidWtf8 => unreachable,
+ };
+ key_wtf16le_buf[key_wtf16le_len] = 0;
+ break :key_wtf16le key_wtf16le_buf[0..key_wtf16le_len :0];
+ };
+
+ const registry_wtf16le = try RegistryWtf16Le.openKey(hkey, key_wtf16le);
+ return RegistryWtf8{ .key = registry_wtf16le.key };
+ }
+
+ /// Closes key, after that usage is invalid
+ pub fn closeKey(self: *const RegistryWtf8) void {
+ const return_code_int: windows.HRESULT = windows.advapi32.RegCloseKey(self.key);
+ const return_code: windows.Win32Error = @enumFromInt(return_code_int);
+ switch (return_code) {
+ .SUCCESS => {},
+ else => {},
+ }
+ }
+
+ /// Get string from registry.
+ /// Caller owns result.
+ pub fn getString(self: *const RegistryWtf8, allocator: std.mem.Allocator, subkey: []const u8, value_name: []const u8) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]u8 {
+ const subkey_wtf16le: [:0]const u16 = subkey_wtf16le: {
+ var subkey_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined;
+ const subkey_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(subkey_wtf16le_buf[0..], subkey) catch unreachable;
+ subkey_wtf16le_buf[subkey_wtf16le_len] = 0;
+ break :subkey_wtf16le subkey_wtf16le_buf[0..subkey_wtf16le_len :0];
+ };
+
+ const value_name_wtf16le: [:0]const u16 = value_name_wtf16le: {
+ var value_name_wtf16le_buf: [RegistryWtf16Le.value_name_max_len]u16 = undefined;
+ const value_name_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(value_name_wtf16le_buf[0..], value_name) catch unreachable;
+ value_name_wtf16le_buf[value_name_wtf16le_len] = 0;
+ break :value_name_wtf16le value_name_wtf16le_buf[0..value_name_wtf16le_len :0];
+ };
+
+ const registry_wtf16le = RegistryWtf16Le{ .key = self.key };
+ const value_wtf16le = try registry_wtf16le.getString(allocator, subkey_wtf16le, value_name_wtf16le);
+ defer allocator.free(value_wtf16le);
+
+ const value_wtf8: []u8 = try std.unicode.wtf16LeToWtf8Alloc(allocator, value_wtf16le);
+ errdefer allocator.free(value_wtf8);
+
+ return value_wtf8;
+ }
+
+ /// Get DWORD (u32) from registry.
+ pub fn getDword(self: *const RegistryWtf8, subkey: []const u8, value_name: []const u8) error{ ValueNameNotFound, NotADword, DwordTooLong, DwordNotFound }!u32 {
+ const subkey_wtf16le: [:0]const u16 = subkey_wtf16le: {
+ var subkey_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined;
+ const subkey_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(subkey_wtf16le_buf[0..], subkey) catch unreachable;
+ subkey_wtf16le_buf[subkey_wtf16le_len] = 0;
+ break :subkey_wtf16le subkey_wtf16le_buf[0..subkey_wtf16le_len :0];
+ };
+
+ const value_name_wtf16le: [:0]const u16 = value_name_wtf16le: {
+ var value_name_wtf16le_buf: [RegistryWtf16Le.value_name_max_len]u16 = undefined;
+ const value_name_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(value_name_wtf16le_buf[0..], value_name) catch unreachable;
+ value_name_wtf16le_buf[value_name_wtf16le_len] = 0;
+ break :value_name_wtf16le value_name_wtf16le_buf[0..value_name_wtf16le_len :0];
+ };
+
+ const registry_wtf16le = RegistryWtf16Le{ .key = self.key };
+ return try registry_wtf16le.getDword(subkey_wtf16le, value_name_wtf16le);
+ }
+
+ /// Under private space with flags:
+ /// KEY_QUERY_VALUE and KEY_ENUMERATE_SUB_KEYS.
+ /// After finishing work, call `closeKey`.
+ pub fn loadFromPath(absolute_path: []const u8) error{KeyNotFound}!RegistryWtf8 {
+ const absolute_path_wtf16le: [:0]const u16 = absolute_path_wtf16le: {
+ var absolute_path_wtf16le_buf: [RegistryWtf16Le.value_name_max_len]u16 = undefined;
+ const absolute_path_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(absolute_path_wtf16le_buf[0..], absolute_path) catch unreachable;
+ absolute_path_wtf16le_buf[absolute_path_wtf16le_len] = 0;
+ break :absolute_path_wtf16le absolute_path_wtf16le_buf[0..absolute_path_wtf16le_len :0];
+ };
+
+ const registry_wtf16le = try RegistryWtf16Le.loadFromPath(absolute_path_wtf16le);
+ return RegistryWtf8{ .key = registry_wtf16le.key };
+ }
+};
+
+const RegistryWtf16Le = struct {
+ key: windows.HKEY,
+
+ /// Includes root key (f.e. HKEY_LOCAL_MACHINE).
+ /// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
+ pub const key_name_max_len = 255;
+ /// In Unicode characters.
+ /// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
+ pub const value_name_max_len = 16_383;
+
+ /// Under HKEY_LOCAL_MACHINE with flags:
+ /// KEY_QUERY_VALUE, KEY_WOW64_32KEY, and KEY_ENUMERATE_SUB_KEYS.
+ /// After finishing work, call `closeKey`.
+ fn openKey(hkey: windows.HKEY, key_wtf16le: [:0]const u16) error{KeyNotFound}!RegistryWtf16Le {
+ var key: windows.HKEY = undefined;
+ const return_code_int: windows.HRESULT = windows.advapi32.RegOpenKeyExW(
+ hkey,
+ key_wtf16le,
+ 0,
+ windows.KEY_QUERY_VALUE | windows.KEY_WOW64_32KEY | windows.KEY_ENUMERATE_SUB_KEYS,
+ &key,
+ );
+ const return_code: windows.Win32Error = @enumFromInt(return_code_int);
+ switch (return_code) {
+ .SUCCESS => {},
+ .FILE_NOT_FOUND => return error.KeyNotFound,
+
+ else => return error.KeyNotFound,
+ }
+ return RegistryWtf16Le{ .key = key };
+ }
+
+ /// Closes key, after that usage is invalid
+ fn closeKey(self: *const RegistryWtf16Le) void {
+ const return_code_int: windows.HRESULT = windows.advapi32.RegCloseKey(self.key);
+ const return_code: windows.Win32Error = @enumFromInt(return_code_int);
+ switch (return_code) {
+ .SUCCESS => {},
+ else => {},
+ }
+ }
+
+ /// Get string ([:0]const u16) from registry.
+ fn getString(self: *const RegistryWtf16Le, allocator: std.mem.Allocator, subkey_wtf16le: [:0]const u16, value_name_wtf16le: [:0]const u16) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]const u16 {
+ var actual_type: windows.ULONG = undefined;
+
+ // Calculating length to allocate
+ var value_wtf16le_buf_size: u32 = 0; // in bytes, including any terminating NUL character or characters.
+ var return_code_int: windows.HRESULT = windows.advapi32.RegGetValueW(
+ self.key,
+ subkey_wtf16le,
+ value_name_wtf16le,
+ RRF.RT_REG_SZ,
+ &actual_type,
+ null,
+ &value_wtf16le_buf_size,
+ );
+
+ // Check returned code and type
+ var return_code: windows.Win32Error = @enumFromInt(return_code_int);
+ switch (return_code) {
+ .SUCCESS => std.debug.assert(value_wtf16le_buf_size != 0),
+ .MORE_DATA => unreachable, // We are only reading length
+ .FILE_NOT_FOUND => return error.ValueNameNotFound,
+ .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY
+ else => return error.StringNotFound,
+ }
+ switch (actual_type) {
+ windows.REG.SZ => {},
+ else => return error.NotAString,
+ }
+
+ const value_wtf16le_buf: []u16 = try allocator.alloc(u16, std.math.divCeil(u32, value_wtf16le_buf_size, 2) catch unreachable);
+ errdefer allocator.free(value_wtf16le_buf);
+
+ return_code_int = windows.advapi32.RegGetValueW(
+ self.key,
+ subkey_wtf16le,
+ value_name_wtf16le,
+ RRF.RT_REG_SZ,
+ &actual_type,
+ value_wtf16le_buf.ptr,
+ &value_wtf16le_buf_size,
+ );
+
+ // Check returned code and (just in case) type again.
+ return_code = @enumFromInt(return_code_int);
+ switch (return_code) {
+ .SUCCESS => {},
+ .MORE_DATA => unreachable, // Calculated first time length should be enough, even overestimated
+ .FILE_NOT_FOUND => return error.ValueNameNotFound,
+ .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY
+ else => return error.StringNotFound,
+ }
+ switch (actual_type) {
+ windows.REG.SZ => {},
+ else => return error.NotAString,
+ }
+
+ const value_wtf16le: []const u16 = value_wtf16le: {
+ // note(bratishkaerik): somehow returned value in `buf_len` is overestimated by Windows and contains extra space
+ // we will just search for zero termination and forget length
+ // Windows sure is strange
+ const value_wtf16le_overestimated: [*:0]const u16 = @ptrCast(value_wtf16le_buf.ptr);
+ break :value_wtf16le std.mem.span(value_wtf16le_overestimated);
+ };
+
+ _ = allocator.resize(value_wtf16le_buf, value_wtf16le.len);
+ return value_wtf16le;
+ }
+
+ /// Get DWORD (u32) from registry.
+ fn getDword(self: *const RegistryWtf16Le, subkey_wtf16le: [:0]const u16, value_name_wtf16le: [:0]const u16) error{ ValueNameNotFound, NotADword, DwordTooLong, DwordNotFound }!u32 {
+ var actual_type: windows.ULONG = undefined;
+ var reg_size: u32 = @sizeOf(u32);
+ var reg_value: u32 = 0;
+
+ const return_code_int: windows.HRESULT = windows.advapi32.RegGetValueW(
+ self.key,
+ subkey_wtf16le,
+ value_name_wtf16le,
+ RRF.RT_REG_DWORD,
+ &actual_type,
+ ®_value,
+ ®_size,
+ );
+ const return_code: windows.Win32Error = @enumFromInt(return_code_int);
+ switch (return_code) {
+ .SUCCESS => {},
+ .MORE_DATA => return error.DwordTooLong,
+ .FILE_NOT_FOUND => return error.ValueNameNotFound,
+ .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY
+ else => return error.DwordNotFound,
+ }
+
+ switch (actual_type) {
+ windows.REG.DWORD => {},
+ else => return error.NotADword,
+ }
+
+ return reg_value;
+ }
+
+ /// Under private space with flags:
+ /// KEY_QUERY_VALUE and KEY_ENUMERATE_SUB_KEYS.
+ /// After finishing work, call `closeKey`.
+ fn loadFromPath(absolute_path_as_wtf16le: [:0]const u16) error{KeyNotFound}!RegistryWtf16Le {
+ var key: windows.HKEY = undefined;
+
+ const return_code_int: windows.HRESULT = std.os.windows.advapi32.RegLoadAppKeyW(
+ absolute_path_as_wtf16le,
+ &key,
+ windows.KEY_QUERY_VALUE | windows.KEY_ENUMERATE_SUB_KEYS,
+ 0,
+ 0,
+ );
+ const return_code: windows.Win32Error = @enumFromInt(return_code_int);
+ switch (return_code) {
+ .SUCCESS => {},
+ else => return error.KeyNotFound,
+ }
+
+ return RegistryWtf16Le{ .key = key };
+ }
+};
+
+pub const Windows10Sdk = struct {
+ path: []const u8,
+ version: []const u8,
+
+ /// Find path and version of Windows 10 SDK.
+ /// Caller owns the result's fields.
+ /// After finishing work, call `free(allocator)`.
+ fn find(allocator: std.mem.Allocator) error{ OutOfMemory, Windows10SdkNotFound, PathTooLong, VersionTooLong }!Windows10Sdk {
+ const v10_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0") catch |err| switch (err) {
+ error.KeyNotFound => return error.Windows10SdkNotFound,
+ };
+ defer v10_key.closeKey();
+
+ const path: []const u8 = path10: {
+ const path_maybe_with_trailing_slash = v10_key.getString(allocator, "", "InstallationFolder") catch |err| switch (err) {
+ error.NotAString => return error.Windows10SdkNotFound,
+ error.ValueNameNotFound => return error.Windows10SdkNotFound,
+ error.StringNotFound => return error.Windows10SdkNotFound,
+
+ error.OutOfMemory => return error.OutOfMemory,
+ };
+
+ if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
+ allocator.free(path_maybe_with_trailing_slash);
+ return error.PathTooLong;
+ }
+
+ var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
+ errdefer path.deinit();
+
+ // String might contain trailing slash, so trim it here
+ if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
+
+ const path_without_trailing_slash = try path.toOwnedSlice();
+ break :path10 path_without_trailing_slash;
+ };
+ errdefer allocator.free(path);
+
+ const version: []const u8 = version10: {
+
+ // note(dimenus): Microsoft doesn't include the .0 in the ProductVersion key....
+ const version_without_0 = v10_key.getString(allocator, "", "ProductVersion") catch |err| switch (err) {
+ error.NotAString => return error.Windows10SdkNotFound,
+ error.ValueNameNotFound => return error.Windows10SdkNotFound,
+ error.StringNotFound => return error.Windows10SdkNotFound,
+
+ error.OutOfMemory => return error.OutOfMemory,
+ };
+ if (version_without_0.len + ".0".len > product_version_max_length) {
+ allocator.free(version_without_0);
+ return error.VersionTooLong;
+ }
+
+ var version = std.ArrayList(u8).fromOwnedSlice(allocator, version_without_0);
+ errdefer version.deinit();
+
+ try version.appendSlice(".0");
+
+ const version_with_0 = try version.toOwnedSlice();
+ break :version10 version_with_0;
+ };
+ errdefer allocator.free(version);
+
+ return Windows10Sdk{ .path = path, .version = version };
+ }
+
+ /// Check whether this version is enumerated in registry.
+ fn isValidVersion(windows10sdk: *const Windows10Sdk) bool {
+ var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
+ const reg_query_as_wtf8 = std.fmt.bufPrint(buf[0..], "{s}\\{s}\\Installed Options", .{ WINDOWS_KIT_REG_KEY, windows10sdk.version }) catch |err| switch (err) {
+ error.NoSpaceLeft => return false,
+ };
+
+ const options_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, reg_query_as_wtf8) catch |err| switch (err) {
+ error.KeyNotFound => return false,
+ };
+ defer options_key.closeKey();
+
+ const option_name = comptime switch (builtin.target.cpu.arch) {
+ .arm, .armeb => "OptionId.DesktopCPParm",
+ .aarch64 => "OptionId.DesktopCPParm64",
+ .x86_64 => "OptionId.DesktopCPPx64",
+ .x86 => "OptionId.DesktopCPPx86",
+ else => |tag| @compileError("Windows 10 SDK cannot be detected on architecture " ++ tag),
+ };
+
+ const reg_value = options_key.getDword("", option_name) catch return false;
+ return (reg_value == 1);
+ }
+
+ fn free(self: *const Windows10Sdk, allocator: std.mem.Allocator) void {
+ allocator.free(self.path);
+ allocator.free(self.version);
+ }
+};
+
+pub const Windows81Sdk = struct {
+ path: []const u8,
+ version: []const u8,
+
+ /// Find path and version of Windows 8.1 SDK.
+ /// Caller owns the result's fields.
+ /// After finishing work, call `free(allocator)`.
+ fn find(allocator: std.mem.Allocator, roots_key: *const RegistryWtf8) error{ OutOfMemory, Windows81SdkNotFound, PathTooLong, VersionTooLong }!Windows81Sdk {
+ const path: []const u8 = path81: {
+ const path_maybe_with_trailing_slash = roots_key.getString(allocator, "", "KitsRoot81") catch |err| switch (err) {
+ error.NotAString => return error.Windows81SdkNotFound,
+ error.ValueNameNotFound => return error.Windows81SdkNotFound,
+ error.StringNotFound => return error.Windows81SdkNotFound,
+
+ error.OutOfMemory => return error.OutOfMemory,
+ };
+ if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
+ allocator.free(path_maybe_with_trailing_slash);
+ return error.PathTooLong;
+ }
+
+ var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
+ errdefer path.deinit();
+
+ // String might contain trailing slash, so trim it here
+ if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
+
+ const path_without_trailing_slash = try path.toOwnedSlice();
+ break :path81 path_without_trailing_slash;
+ };
+ errdefer allocator.free(path);
+
+ const version: []const u8 = version81: {
+ var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
+ const sdk_lib_dir_path = std.fmt.bufPrint(buf[0..], "{s}\\Lib\\", .{path}) catch |err| switch (err) {
+ error.NoSpaceLeft => return error.PathTooLong,
+ };
+ if (!std.fs.path.isAbsolute(sdk_lib_dir_path)) return error.Windows81SdkNotFound;
+
+ // enumerate files in sdk path looking for latest version
+ var sdk_lib_dir = std.fs.openDirAbsolute(sdk_lib_dir_path, .{
+ .iterate = true,
+ }) catch |err| switch (err) {
+ error.NameTooLong => return error.PathTooLong,
+ else => return error.Windows81SdkNotFound,
+ };
+ defer sdk_lib_dir.close();
+
+ var iterator = sdk_lib_dir.iterate();
+ const versions = iterateAndFilterBySemVer(&iterator, allocator, "winv") catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.VersionNotFound => return error.Windows81SdkNotFound,
+ };
+ defer {
+ for (versions) |version| allocator.free(version);
+ allocator.free(versions);
+ }
+ const latest_version = try allocator.dupe(u8, versions[0]);
+ break :version81 latest_version;
+ };
+ errdefer allocator.free(version);
+
+ return Windows81Sdk{ .path = path, .version = version };
+ }
+
+ fn free(self: *const Windows81Sdk, allocator: std.mem.Allocator) void {
+ allocator.free(self.path);
+ allocator.free(self.version);
+ }
+};
+
+const MsvcLibDir = struct {
+ fn findInstancesDirViaCLSID(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {
+ const setup_configuration_clsid = "{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}";
+ const setup_config_key = RegistryWtf8.openKey(windows.HKEY_CLASSES_ROOT, "CLSID\\" ++ setup_configuration_clsid) catch |err| switch (err) {
+ error.KeyNotFound => return error.PathNotFound,
+ };
+ defer setup_config_key.closeKey();
+
+ const dll_path = setup_config_key.getString(allocator, "InprocServer32", "") catch |err| switch (err) {
+ error.NotAString,
+ error.ValueNameNotFound,
+ error.StringNotFound,
+ => return error.PathNotFound,
+
+ error.OutOfMemory => return error.OutOfMemory,
+ };
+ defer allocator.free(dll_path);
+
+ var path_it = std.fs.path.componentIterator(dll_path) catch return error.PathNotFound;
+ // the .dll filename
+ _ = path_it.last();
+ const root_path = while (path_it.previous()) |dir_component| {
+ if (std.ascii.eqlIgnoreCase(dir_component.name, "VisualStudio")) {
+ break dir_component.path;
+ }
+ } else {
+ return error.PathNotFound;
+ };
+
+ const instances_path = try std.fs.path.join(allocator, &.{ root_path, "Packages", "_Instances" });
+ defer allocator.free(instances_path);
+
+ return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return error.PathNotFound;
+ }
+
+ fn findInstancesDir(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {
+ // First try to get the path from the .dll that would have been
+ // loaded via COM for SetupConfiguration.
+ return findInstancesDirViaCLSID(allocator) catch |orig_err| {
+ // If that can't be found, fall back to manually appending
+ // `Microsoft\VisualStudio\Packages\_Instances` to %PROGRAMDATA%
+ const program_data = std.process.getEnvVarOwned(allocator, "PROGRAMDATA") catch |err| switch (err) {
+ error.OutOfMemory => |e| return e,
+ else => return orig_err,
+ };
+ defer allocator.free(program_data);
+
+ const instances_path = try std.fs.path.join(allocator, &.{ program_data, "Microsoft", "VisualStudio", "Packages", "_Instances" });
+ defer allocator.free(instances_path);
+
+ return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return orig_err;
+ };
+ }
+
+ /// Intended to be equivalent to `ISetupHelper.ParseVersion`
+ /// Example: 17.4.33205.214 -> 0x0011000481b500d6
+ fn parseVersionQuad(version: []const u8) error{InvalidVersion}!u64 {
+ var it = std.mem.splitScalar(u8, version, '.');
+ const a = it.next() orelse return error.InvalidVersion;
+ const b = it.next() orelse return error.InvalidVersion;
+ const c = it.next() orelse return error.InvalidVersion;
+ const d = it.next() orelse return error.InvalidVersion;
+ if (it.next()) |_| return error.InvalidVersion;
+ var result: u64 = undefined;
+ var result_bytes = std.mem.asBytes(&result);
+
+ std.mem.writeInt(
+ u16,
+ result_bytes[0..2],
+ std.fmt.parseUnsigned(u16, d, 10) catch return error.InvalidVersion,
+ .little,
+ );
+ std.mem.writeInt(
+ u16,
+ result_bytes[2..4],
+ std.fmt.parseUnsigned(u16, c, 10) catch return error.InvalidVersion,
+ .little,
+ );
+ std.mem.writeInt(
+ u16,
+ result_bytes[4..6],
+ std.fmt.parseUnsigned(u16, b, 10) catch return error.InvalidVersion,
+ .little,
+ );
+ std.mem.writeInt(
+ u16,
+ result_bytes[6..8],
+ std.fmt.parseUnsigned(u16, a, 10) catch return error.InvalidVersion,
+ .little,
+ );
+
+ return result;
+ }
+
+ /// Intended to be equivalent to ISetupConfiguration.EnumInstances:
+ /// https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.setup.configuration
+ /// but without the use of COM in order to avoid a dependency on ole32.dll
+ ///
+ /// The logic in this function is intended to match what ISetupConfiguration does
+ /// under-the-hood, as verified using Procmon.
+ fn findViaCOM(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
+ // Typically `%PROGRAMDATA%\Microsoft\VisualStudio\Packages\_Instances`
+ // This will contain directories with names of instance IDs like 80a758ca,
+ // which will contain `state.json` files that have the version and
+ // installation directory.
+ var instances_dir = try findInstancesDir(allocator);
+ defer instances_dir.close();
+
+ var state_subpath_buf: [std.fs.MAX_NAME_BYTES + 32]u8 = undefined;
+ var latest_version_lib_dir = std.ArrayListUnmanaged(u8){};
+ errdefer latest_version_lib_dir.deinit(allocator);
+
+ var latest_version: u64 = 0;
+ var instances_dir_it = instances_dir.iterateAssumeFirstIteration();
+ while (instances_dir_it.next() catch return error.PathNotFound) |entry| {
+ if (entry.kind != .directory) continue;
+
+ var fbs = std.io.fixedBufferStream(&state_subpath_buf);
+ const writer = fbs.writer();
+
+ writer.writeAll(entry.name) catch unreachable;
+ writer.writeByte(std.fs.path.sep) catch unreachable;
+ writer.writeAll("state.json") catch unreachable;
+
+ const json_contents = instances_dir.readFileAlloc(allocator, fbs.getWritten(), std.math.maxInt(usize)) catch continue;
+ defer allocator.free(json_contents);
+
+ var parsed = std.json.parseFromSlice(std.json.Value, allocator, json_contents, .{}) catch continue;
+ defer parsed.deinit();
+
+ if (parsed.value != .object) continue;
+ const catalog_info = parsed.value.object.get("catalogInfo") orelse continue;
+ if (catalog_info != .object) continue;
+ const product_version_value = catalog_info.object.get("buildVersion") orelse continue;
+ if (product_version_value != .string) continue;
+ const product_version_text = product_version_value.string;
+ const parsed_version = parseVersionQuad(product_version_text) catch continue;
+
+ // We want to end up with the most recent version installed
+ if (parsed_version <= latest_version) continue;
+
+ const installation_path = parsed.value.object.get("installationPath") orelse continue;
+ if (installation_path != .string) continue;
+
+ const lib_dir_path = libDirFromInstallationPath(allocator, installation_path.string) catch |err| switch (err) {
+ error.OutOfMemory => |e| return e,
+ error.PathNotFound => continue,
+ };
+ defer allocator.free(lib_dir_path);
+
+ latest_version_lib_dir.clearRetainingCapacity();
+ try latest_version_lib_dir.appendSlice(allocator, lib_dir_path);
+ latest_version = parsed_version;
+ }
+
+ if (latest_version_lib_dir.items.len == 0) return error.PathNotFound;
+ return latest_version_lib_dir.toOwnedSlice(allocator);
+ }
+
+ fn libDirFromInstallationPath(allocator: std.mem.Allocator, installation_path: []const u8) error{ OutOfMemory, PathNotFound }![]const u8 {
+ var lib_dir_buf = try std.ArrayList(u8).initCapacity(allocator, installation_path.len + 64);
+ errdefer lib_dir_buf.deinit();
+
+ lib_dir_buf.appendSliceAssumeCapacity(installation_path);
+
+ if (!std.fs.path.isSep(lib_dir_buf.getLast())) {
+ try lib_dir_buf.append('\\');
+ }
+ const installation_path_with_trailing_sep_len = lib_dir_buf.items.len;
+
+ try lib_dir_buf.appendSlice("VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
+ var default_tools_version_buf: [512]u8 = undefined;
+ const default_tools_version_contents = std.fs.cwd().readFile(lib_dir_buf.items, &default_tools_version_buf) catch {
+ return error.PathNotFound;
+ };
+ var tokenizer = std.mem.tokenizeAny(u8, default_tools_version_contents, " \r\n");
+ const default_tools_version = tokenizer.next() orelse return error.PathNotFound;
+
+ lib_dir_buf.shrinkRetainingCapacity(installation_path_with_trailing_sep_len);
+ try lib_dir_buf.appendSlice("VC\\Tools\\MSVC\\");
+ try lib_dir_buf.appendSlice(default_tools_version);
+ const folder_with_arch = "\\Lib\\" ++ comptime switch (builtin.target.cpu.arch) {
+ .x86 => "x86",
+ .x86_64 => "x64",
+ .arm, .armeb => "arm",
+ .aarch64 => "arm64",
+ else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag),
+ };
+ try lib_dir_buf.appendSlice(folder_with_arch);
+
+ if (!verifyLibDir(lib_dir_buf.items)) {
+ return error.PathNotFound;
+ }
+
+ return lib_dir_buf.toOwnedSlice();
+ }
+
+ // https://learn.microsoft.com/en-us/visualstudio/install/tools-for-managing-visual-studio-instances?view=vs-2022#editing-the-registry-for-a-visual-studio-instance
+ fn findViaRegistry(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
+
+ // %localappdata%\Microsoft\VisualStudio\
+ // %appdata%\Local\Microsoft\VisualStudio\
+ const visualstudio_folder_path = std.fs.getAppDataDir(allocator, "Microsoft\\VisualStudio\\") catch return error.PathNotFound;
+ defer allocator.free(visualstudio_folder_path);
+
+ const vs_versions: []const []const u8 = vs_versions: {
+ if (!std.fs.path.isAbsolute(visualstudio_folder_path)) return error.PathNotFound;
+ // enumerate folders that contain `privateregistry.bin`, looking for all versions
+ // f.i. %localappdata%\Microsoft\VisualStudio\17.0_9e9cbb98\
+ var visualstudio_folder = std.fs.openDirAbsolute(visualstudio_folder_path, .{
+ .iterate = true,
+ }) catch return error.PathNotFound;
+ defer visualstudio_folder.close();
+
+ var iterator = visualstudio_folder.iterate();
+ const versions = iterateAndFilterBySemVer(&iterator, allocator, null) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.VersionNotFound => return error.PathNotFound,
+ };
+ break :vs_versions versions;
+ };
+ defer {
+ for (vs_versions) |vs_version| allocator.free(vs_version);
+ allocator.free(vs_versions);
+ }
+ var config_subkey_buf: [RegistryWtf16Le.key_name_max_len * 2]u8 = undefined;
+ const source_directories: []const u8 = source_directories: for (vs_versions) |vs_version| {
+ const privateregistry_absolute_path = std.fs.path.join(allocator, &.{ visualstudio_folder_path, vs_version, "privateregistry.bin" }) catch continue;
+ defer allocator.free(privateregistry_absolute_path);
+ if (!std.fs.path.isAbsolute(privateregistry_absolute_path)) continue;
+
+ const visualstudio_registry = RegistryWtf8.loadFromPath(privateregistry_absolute_path) catch continue;
+ defer visualstudio_registry.closeKey();
+
+ const config_subkey = std.fmt.bufPrint(config_subkey_buf[0..], "Software\\Microsoft\\VisualStudio\\{s}_Config", .{vs_version}) catch unreachable;
+
+ const source_directories_value = visualstudio_registry.getString(allocator, config_subkey, "Source Directories") catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ else => continue,
+ };
+ if (source_directories_value.len > (std.fs.MAX_PATH_BYTES * 30)) { // note(bratishkaerik): guessing from the fact that on my computer it has 15 pathes and at least some of them are not of max length
+ allocator.free(source_directories_value);
+ continue;
+ }
+
+ break :source_directories source_directories_value;
+ } else return error.PathNotFound;
+ defer allocator.free(source_directories);
+
+ var source_directories_splitted = std.mem.splitScalar(u8, source_directories, ';');
+
+ const msvc_dir: []const u8 = msvc_dir: {
+ const msvc_include_dir_maybe_with_trailing_slash = try allocator.dupe(u8, source_directories_splitted.first());
+
+ if (msvc_include_dir_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(msvc_include_dir_maybe_with_trailing_slash)) {
+ allocator.free(msvc_include_dir_maybe_with_trailing_slash);
+ return error.PathNotFound;
+ }
+
+ var msvc_dir = std.ArrayList(u8).fromOwnedSlice(allocator, msvc_include_dir_maybe_with_trailing_slash);
+ errdefer msvc_dir.deinit();
+
+ // String might contain trailing slash, so trim it here
+ if (msvc_dir.items.len > "C:\\".len and msvc_dir.getLast() == '\\') _ = msvc_dir.pop();
+
+ // Remove `\include` at the end of path
+ if (std.mem.endsWith(u8, msvc_dir.items, "\\include")) {
+ msvc_dir.shrinkRetainingCapacity(msvc_dir.items.len - "\\include".len);
+ }
+
+ const folder_with_arch = "\\Lib\\" ++ comptime switch (builtin.target.cpu.arch) {
+ .x86 => "x86",
+ .x86_64 => "x64",
+ .arm, .armeb => "arm",
+ .aarch64 => "arm64",
+ else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag),
+ };
+
+ try msvc_dir.appendSlice(folder_with_arch);
+ const msvc_dir_with_arch = try msvc_dir.toOwnedSlice();
+ break :msvc_dir msvc_dir_with_arch;
+ };
+ errdefer allocator.free(msvc_dir);
+
+ if (!verifyLibDir(msvc_dir)) {
+ return error.PathNotFound;
+ }
+
+ return msvc_dir;
+ }
+
+ fn findViaVs7Key(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
+ var base_path: std.ArrayList(u8) = base_path: {
+ try_env: {
+ var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ else => break :try_env,
+ };
+ defer env_map.deinit();
+
+ if (env_map.get("VS140COMNTOOLS")) |VS140COMNTOOLS| {
+ if (VS140COMNTOOLS.len < "C:\\Common7\\Tools".len) break :try_env;
+ if (!std.fs.path.isAbsolute(VS140COMNTOOLS)) break :try_env;
+ var list = std.ArrayList(u8).init(allocator);
+ errdefer list.deinit();
+
+ try list.appendSlice(VS140COMNTOOLS); // C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools
+ // String might contain trailing slash, so trim it here
+ if (list.items.len > "C:\\".len and list.getLast() == '\\') _ = list.pop();
+ list.shrinkRetainingCapacity(list.items.len - "\\Common7\\Tools".len); // C:\Program Files (x86)\Microsoft Visual Studio 14.0
+ break :base_path list;
+ }
+ }
+
+ const vs7_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7") catch return error.PathNotFound;
+ defer vs7_key.closeKey();
+ try_vs7_key: {
+ const path_maybe_with_trailing_slash = vs7_key.getString(allocator, "", "14.0") catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ else => break :try_vs7_key,
+ };
+
+ if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
+ allocator.free(path_maybe_with_trailing_slash);
+ break :try_vs7_key;
+ }
+
+ var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
+ errdefer path.deinit();
+
+ // String might contain trailing slash, so trim it here
+ if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
+ break :base_path path;
+ }
+ return error.PathNotFound;
+ };
+ errdefer base_path.deinit();
+
+ const folder_with_arch = "\\VC\\lib\\" ++ comptime switch (builtin.target.cpu.arch) {
+ .x86 => "", //x86 is in the root of the Lib folder
+ .x86_64 => "amd64",
+ .arm, .armeb => "arm",
+ .aarch64 => "arm64",
+ else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag),
+ };
+ try base_path.appendSlice(folder_with_arch);
+
+ if (!verifyLibDir(base_path.items)) {
+ return error.PathNotFound;
+ }
+
+ const full_path = try base_path.toOwnedSlice();
+ return full_path;
+ }
+
+ fn verifyLibDir(lib_dir_path: []const u8) bool {
+ std.debug.assert(std.fs.path.isAbsolute(lib_dir_path)); // should be already handled in `findVia*`
+
+ var dir = std.fs.openDirAbsolute(lib_dir_path, .{}) catch return false;
+ defer dir.close();
+
+ const stat = dir.statFile("vcruntime.lib") catch return false;
+ if (stat.kind != .file)
+ return false;
+
+ return true;
+ }
+
+ /// Find path to MSVC's `lib/` directory.
+ /// Caller owns the result.
+ pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, MsvcLibDirNotFound }![]const u8 {
+ const full_path = MsvcLibDir.findViaCOM(allocator) catch |err1| switch (err1) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.PathNotFound => MsvcLibDir.findViaRegistry(allocator) catch |err2| switch (err2) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.PathNotFound => MsvcLibDir.findViaVs7Key(allocator) catch |err3| switch (err3) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.PathNotFound => return error.MsvcLibDirNotFound,
+ },
+ },
+ };
+ errdefer allocator.free(full_path);
+
+ return full_path;
+ }
+};
diff --git a/lib/std/zig/target.zig b/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");
diff --git a/src/Compilation.zig b/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
diff --git a/src/glibc.zig b/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| {
diff --git a/src/introspect.zig b/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));
- }
-};
diff --git a/src/libc_installation.zig b/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);
- }
-}
diff --git a/src/link.zig b/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");
diff --git a/src/link/MachO.zig b/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",
diff --git a/src/main.zig b/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) });
diff --git a/src/musl.zig b/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";
diff --git a/src/print_env.zig b/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();
diff --git a/src/print_targets.zig b/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),
});
diff --git a/src/target.zig b/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,
diff --git a/src/wasi_libc.zig b/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";
diff --git a/src/windows_sdk.zig b/src/windows_sdk.zig
@@ -1,965 +0,0 @@
-const std = @import("std");
-const builtin = @import("builtin");
-
-const windows = std.os.windows;
-const RRF = windows.advapi32.RRF;
-
-const WINDOWS_KIT_REG_KEY = "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots";
-
-// https://learn.microsoft.com/en-us/windows/win32/msi/productversion
-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;
-
-/// 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.
-fn iterateAndFilterBySemVer(
- iterator: *std.fs.Dir.Iterator,
- allocator: std.mem.Allocator,
- comptime optional_prefix: ?[]const u8,
-) error{ OutOfMemory, VersionNotFound }![][]const u8 {
- var dirs_filtered_list = std.ArrayList([]const u8).init(allocator);
- errdefer {
- for (dirs_filtered_list.items) |filtered_dir| allocator.free(filtered_dir);
- dirs_filtered_list.deinit();
- }
-
- var normalized_name_buf: [std.fs.MAX_NAME_BYTES + ".0+build.0".len]u8 = undefined;
- var normalized_name_fbs = std.io.fixedBufferStream(&normalized_name_buf);
- const normalized_name_w = normalized_name_fbs.writer();
- iterate_folder: while (true) : (normalized_name_fbs.reset()) {
- const maybe_entry = iterator.next() catch continue :iterate_folder;
- const entry = maybe_entry orelse break :iterate_folder;
-
- if (entry.kind != .directory)
- continue :iterate_folder;
-
- // invalidated on next iteration
- const subfolder_name = blk: {
- if (comptime optional_prefix) |prefix| {
- if (!std.mem.startsWith(u8, entry.name, prefix)) continue :iterate_folder;
- break :blk entry.name[prefix.len..];
- } else break :blk entry.name;
- };
-
- { // check if subfolder name looks similar to SemVer
- switch (std.mem.count(u8, subfolder_name, ".")) {
- 0 => normalized_name_w.print("{s}.0.0+build.0", .{subfolder_name}) catch unreachable, // 17 => 17.0.0+build.0
- 1 => if (std.mem.indexOfScalar(u8, subfolder_name, '_')) |underscore_pos| blk: { // 17.0_9e9cbb98 => 17.0.1+build.9e9cbb98
- var subfolder_name_tmp_copy_buf: [std.fs.MAX_NAME_BYTES]u8 = undefined;
- const subfolder_name_tmp_copy = subfolder_name_tmp_copy_buf[0..subfolder_name.len];
- @memcpy(subfolder_name_tmp_copy, subfolder_name);
-
- subfolder_name_tmp_copy[underscore_pos] = '.'; // 17.0_9e9cbb98 => 17.0.9e9cbb98
- var subfolder_name_parts = std.mem.splitScalar(u8, subfolder_name_tmp_copy, '.'); // [ 17, 0, 9e9cbb98 ]
-
- const first = subfolder_name_parts.first(); // 17
- const second = subfolder_name_parts.next().?; // 0
- const third = subfolder_name_parts.rest(); // 9e9cbb98
-
- break :blk normalized_name_w.print("{s}.{s}.1+build.{s}", .{ first, second, third }) catch unreachable; // [ 17, 0, 9e9cbb98 ] => 17.0.1+build.9e9cbb98
- } else normalized_name_w.print("{s}.0+build.0", .{subfolder_name}) catch unreachable, // 17.0 => 17.0.0+build.0
- else => normalized_name_w.print("{s}+build.0", .{subfolder_name}) catch unreachable, // 17.0.0 => 17.0.0+build.0
- }
- const subfolder_name_normalized: []const u8 = normalized_name_fbs.getWritten();
- const sem_ver = std.SemanticVersion.parse(subfolder_name_normalized);
- _ = sem_ver catch continue :iterate_folder;
- }
- // entry.name passed check
-
- const subfolder_name_allocated = try allocator.dupe(u8, subfolder_name);
- errdefer allocator.free(subfolder_name_allocated);
- try dirs_filtered_list.append(subfolder_name_allocated);
- }
-
- const dirs_filtered_slice = try dirs_filtered_list.toOwnedSlice();
- // Keep in mind that order of these names is not guaranteed by Windows,
- // so we cannot just reverse or "while (popOrNull())" this ArrayList.
- std.mem.sortUnstable([]const u8, dirs_filtered_slice, {}, struct {
- fn desc(_: void, lhs: []const u8, rhs: []const u8) bool {
- return std.mem.order(u8, lhs, rhs) == .gt;
- }
- }.desc);
- return dirs_filtered_slice;
-}
-
-const RegistryWtf8 = struct {
- key: windows.HKEY,
-
- /// Assert that `key` is valid WTF-8 string
- pub fn openKey(hkey: windows.HKEY, key: []const u8) error{KeyNotFound}!RegistryWtf8 {
- const key_wtf16le: [:0]const u16 = key_wtf16le: {
- var key_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined;
- const key_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(key_wtf16le_buf[0..], key) catch |err| switch (err) {
- error.InvalidWtf8 => unreachable,
- };
- key_wtf16le_buf[key_wtf16le_len] = 0;
- break :key_wtf16le key_wtf16le_buf[0..key_wtf16le_len :0];
- };
-
- const registry_wtf16le = try RegistryWtf16Le.openKey(hkey, key_wtf16le);
- return RegistryWtf8{ .key = registry_wtf16le.key };
- }
-
- /// Closes key, after that usage is invalid
- pub fn closeKey(self: *const RegistryWtf8) void {
- const return_code_int: windows.HRESULT = windows.advapi32.RegCloseKey(self.key);
- const return_code: windows.Win32Error = @enumFromInt(return_code_int);
- switch (return_code) {
- .SUCCESS => {},
- else => {},
- }
- }
-
- /// Get string from registry.
- /// Caller owns result.
- pub fn getString(self: *const RegistryWtf8, allocator: std.mem.Allocator, subkey: []const u8, value_name: []const u8) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]u8 {
- const subkey_wtf16le: [:0]const u16 = subkey_wtf16le: {
- var subkey_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined;
- const subkey_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(subkey_wtf16le_buf[0..], subkey) catch unreachable;
- subkey_wtf16le_buf[subkey_wtf16le_len] = 0;
- break :subkey_wtf16le subkey_wtf16le_buf[0..subkey_wtf16le_len :0];
- };
-
- const value_name_wtf16le: [:0]const u16 = value_name_wtf16le: {
- var value_name_wtf16le_buf: [RegistryWtf16Le.value_name_max_len]u16 = undefined;
- const value_name_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(value_name_wtf16le_buf[0..], value_name) catch unreachable;
- value_name_wtf16le_buf[value_name_wtf16le_len] = 0;
- break :value_name_wtf16le value_name_wtf16le_buf[0..value_name_wtf16le_len :0];
- };
-
- const registry_wtf16le = RegistryWtf16Le{ .key = self.key };
- const value_wtf16le = try registry_wtf16le.getString(allocator, subkey_wtf16le, value_name_wtf16le);
- defer allocator.free(value_wtf16le);
-
- const value_wtf8: []u8 = try std.unicode.wtf16LeToWtf8Alloc(allocator, value_wtf16le);
- errdefer allocator.free(value_wtf8);
-
- return value_wtf8;
- }
-
- /// Get DWORD (u32) from registry.
- pub fn getDword(self: *const RegistryWtf8, subkey: []const u8, value_name: []const u8) error{ ValueNameNotFound, NotADword, DwordTooLong, DwordNotFound }!u32 {
- const subkey_wtf16le: [:0]const u16 = subkey_wtf16le: {
- var subkey_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined;
- const subkey_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(subkey_wtf16le_buf[0..], subkey) catch unreachable;
- subkey_wtf16le_buf[subkey_wtf16le_len] = 0;
- break :subkey_wtf16le subkey_wtf16le_buf[0..subkey_wtf16le_len :0];
- };
-
- const value_name_wtf16le: [:0]const u16 = value_name_wtf16le: {
- var value_name_wtf16le_buf: [RegistryWtf16Le.value_name_max_len]u16 = undefined;
- const value_name_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(value_name_wtf16le_buf[0..], value_name) catch unreachable;
- value_name_wtf16le_buf[value_name_wtf16le_len] = 0;
- break :value_name_wtf16le value_name_wtf16le_buf[0..value_name_wtf16le_len :0];
- };
-
- const registry_wtf16le = RegistryWtf16Le{ .key = self.key };
- return try registry_wtf16le.getDword(subkey_wtf16le, value_name_wtf16le);
- }
-
- /// Under private space with flags:
- /// KEY_QUERY_VALUE and KEY_ENUMERATE_SUB_KEYS.
- /// After finishing work, call `closeKey`.
- pub fn loadFromPath(absolute_path: []const u8) error{KeyNotFound}!RegistryWtf8 {
- const absolute_path_wtf16le: [:0]const u16 = absolute_path_wtf16le: {
- var absolute_path_wtf16le_buf: [RegistryWtf16Le.value_name_max_len]u16 = undefined;
- const absolute_path_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(absolute_path_wtf16le_buf[0..], absolute_path) catch unreachable;
- absolute_path_wtf16le_buf[absolute_path_wtf16le_len] = 0;
- break :absolute_path_wtf16le absolute_path_wtf16le_buf[0..absolute_path_wtf16le_len :0];
- };
-
- const registry_wtf16le = try RegistryWtf16Le.loadFromPath(absolute_path_wtf16le);
- return RegistryWtf8{ .key = registry_wtf16le.key };
- }
-};
-
-const RegistryWtf16Le = struct {
- key: windows.HKEY,
-
- /// Includes root key (f.e. HKEY_LOCAL_MACHINE).
- /// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
- pub const key_name_max_len = 255;
- /// In Unicode characters.
- /// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
- pub const value_name_max_len = 16_383;
-
- /// Under HKEY_LOCAL_MACHINE with flags:
- /// KEY_QUERY_VALUE, KEY_WOW64_32KEY, and KEY_ENUMERATE_SUB_KEYS.
- /// After finishing work, call `closeKey`.
- fn openKey(hkey: windows.HKEY, key_wtf16le: [:0]const u16) error{KeyNotFound}!RegistryWtf16Le {
- var key: windows.HKEY = undefined;
- const return_code_int: windows.HRESULT = windows.advapi32.RegOpenKeyExW(
- hkey,
- key_wtf16le,
- 0,
- windows.KEY_QUERY_VALUE | windows.KEY_WOW64_32KEY | windows.KEY_ENUMERATE_SUB_KEYS,
- &key,
- );
- const return_code: windows.Win32Error = @enumFromInt(return_code_int);
- switch (return_code) {
- .SUCCESS => {},
- .FILE_NOT_FOUND => return error.KeyNotFound,
-
- else => return error.KeyNotFound,
- }
- return RegistryWtf16Le{ .key = key };
- }
-
- /// Closes key, after that usage is invalid
- fn closeKey(self: *const RegistryWtf16Le) void {
- const return_code_int: windows.HRESULT = windows.advapi32.RegCloseKey(self.key);
- const return_code: windows.Win32Error = @enumFromInt(return_code_int);
- switch (return_code) {
- .SUCCESS => {},
- else => {},
- }
- }
-
- /// Get string ([:0]const u16) from registry.
- fn getString(self: *const RegistryWtf16Le, allocator: std.mem.Allocator, subkey_wtf16le: [:0]const u16, value_name_wtf16le: [:0]const u16) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]const u16 {
- var actual_type: windows.ULONG = undefined;
-
- // Calculating length to allocate
- var value_wtf16le_buf_size: u32 = 0; // in bytes, including any terminating NUL character or characters.
- var return_code_int: windows.HRESULT = windows.advapi32.RegGetValueW(
- self.key,
- subkey_wtf16le,
- value_name_wtf16le,
- RRF.RT_REG_SZ,
- &actual_type,
- null,
- &value_wtf16le_buf_size,
- );
-
- // Check returned code and type
- var return_code: windows.Win32Error = @enumFromInt(return_code_int);
- switch (return_code) {
- .SUCCESS => std.debug.assert(value_wtf16le_buf_size != 0),
- .MORE_DATA => unreachable, // We are only reading length
- .FILE_NOT_FOUND => return error.ValueNameNotFound,
- .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY
- else => return error.StringNotFound,
- }
- switch (actual_type) {
- windows.REG.SZ => {},
- else => return error.NotAString,
- }
-
- const value_wtf16le_buf: []u16 = try allocator.alloc(u16, std.math.divCeil(u32, value_wtf16le_buf_size, 2) catch unreachable);
- errdefer allocator.free(value_wtf16le_buf);
-
- return_code_int = windows.advapi32.RegGetValueW(
- self.key,
- subkey_wtf16le,
- value_name_wtf16le,
- RRF.RT_REG_SZ,
- &actual_type,
- value_wtf16le_buf.ptr,
- &value_wtf16le_buf_size,
- );
-
- // Check returned code and (just in case) type again.
- return_code = @enumFromInt(return_code_int);
- switch (return_code) {
- .SUCCESS => {},
- .MORE_DATA => unreachable, // Calculated first time length should be enough, even overestimated
- .FILE_NOT_FOUND => return error.ValueNameNotFound,
- .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY
- else => return error.StringNotFound,
- }
- switch (actual_type) {
- windows.REG.SZ => {},
- else => return error.NotAString,
- }
-
- const value_wtf16le: []const u16 = value_wtf16le: {
- // note(bratishkaerik): somehow returned value in `buf_len` is overestimated by Windows and contains extra space
- // we will just search for zero termination and forget length
- // Windows sure is strange
- const value_wtf16le_overestimated: [*:0]const u16 = @ptrCast(value_wtf16le_buf.ptr);
- break :value_wtf16le std.mem.span(value_wtf16le_overestimated);
- };
-
- _ = allocator.resize(value_wtf16le_buf, value_wtf16le.len);
- return value_wtf16le;
- }
-
- /// Get DWORD (u32) from registry.
- fn getDword(self: *const RegistryWtf16Le, subkey_wtf16le: [:0]const u16, value_name_wtf16le: [:0]const u16) error{ ValueNameNotFound, NotADword, DwordTooLong, DwordNotFound }!u32 {
- var actual_type: windows.ULONG = undefined;
- var reg_size: u32 = @sizeOf(u32);
- var reg_value: u32 = 0;
-
- const return_code_int: windows.HRESULT = windows.advapi32.RegGetValueW(
- self.key,
- subkey_wtf16le,
- value_name_wtf16le,
- RRF.RT_REG_DWORD,
- &actual_type,
- ®_value,
- ®_size,
- );
- const return_code: windows.Win32Error = @enumFromInt(return_code_int);
- switch (return_code) {
- .SUCCESS => {},
- .MORE_DATA => return error.DwordTooLong,
- .FILE_NOT_FOUND => return error.ValueNameNotFound,
- .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY
- else => return error.DwordNotFound,
- }
-
- switch (actual_type) {
- windows.REG.DWORD => {},
- else => return error.NotADword,
- }
-
- return reg_value;
- }
-
- /// Under private space with flags:
- /// KEY_QUERY_VALUE and KEY_ENUMERATE_SUB_KEYS.
- /// After finishing work, call `closeKey`.
- fn loadFromPath(absolute_path_as_wtf16le: [:0]const u16) error{KeyNotFound}!RegistryWtf16Le {
- var key: windows.HKEY = undefined;
-
- const return_code_int: windows.HRESULT = std.os.windows.advapi32.RegLoadAppKeyW(
- absolute_path_as_wtf16le,
- &key,
- windows.KEY_QUERY_VALUE | windows.KEY_ENUMERATE_SUB_KEYS,
- 0,
- 0,
- );
- const return_code: windows.Win32Error = @enumFromInt(return_code_int);
- switch (return_code) {
- .SUCCESS => {},
- else => return error.KeyNotFound,
- }
-
- return RegistryWtf16Le{ .key = key };
- }
-};
-
-pub const Windows10Sdk = struct {
- path: []const u8,
- version: []const u8,
-
- /// Find path and version of Windows 10 SDK.
- /// Caller owns the result's fields.
- /// After finishing work, call `free(allocator)`.
- fn find(allocator: std.mem.Allocator) error{ OutOfMemory, Windows10SdkNotFound, PathTooLong, VersionTooLong }!Windows10Sdk {
- const v10_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0") catch |err| switch (err) {
- error.KeyNotFound => return error.Windows10SdkNotFound,
- };
- defer v10_key.closeKey();
-
- const path: []const u8 = path10: {
- const path_maybe_with_trailing_slash = v10_key.getString(allocator, "", "InstallationFolder") catch |err| switch (err) {
- error.NotAString => return error.Windows10SdkNotFound,
- error.ValueNameNotFound => return error.Windows10SdkNotFound,
- error.StringNotFound => return error.Windows10SdkNotFound,
-
- error.OutOfMemory => return error.OutOfMemory,
- };
-
- if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
- allocator.free(path_maybe_with_trailing_slash);
- return error.PathTooLong;
- }
-
- var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
- errdefer path.deinit();
-
- // String might contain trailing slash, so trim it here
- if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
-
- const path_without_trailing_slash = try path.toOwnedSlice();
- break :path10 path_without_trailing_slash;
- };
- errdefer allocator.free(path);
-
- const version: []const u8 = version10: {
-
- // note(dimenus): Microsoft doesn't include the .0 in the ProductVersion key....
- const version_without_0 = v10_key.getString(allocator, "", "ProductVersion") catch |err| switch (err) {
- error.NotAString => return error.Windows10SdkNotFound,
- error.ValueNameNotFound => return error.Windows10SdkNotFound,
- error.StringNotFound => return error.Windows10SdkNotFound,
-
- error.OutOfMemory => return error.OutOfMemory,
- };
- if (version_without_0.len + ".0".len > product_version_max_length) {
- allocator.free(version_without_0);
- return error.VersionTooLong;
- }
-
- var version = std.ArrayList(u8).fromOwnedSlice(allocator, version_without_0);
- errdefer version.deinit();
-
- try version.appendSlice(".0");
-
- const version_with_0 = try version.toOwnedSlice();
- break :version10 version_with_0;
- };
- errdefer allocator.free(version);
-
- return Windows10Sdk{ .path = path, .version = version };
- }
-
- /// Check whether this version is enumerated in registry.
- fn isValidVersion(windows10sdk: *const Windows10Sdk) bool {
- var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
- const reg_query_as_wtf8 = std.fmt.bufPrint(buf[0..], "{s}\\{s}\\Installed Options", .{ WINDOWS_KIT_REG_KEY, windows10sdk.version }) catch |err| switch (err) {
- error.NoSpaceLeft => return false,
- };
-
- const options_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, reg_query_as_wtf8) catch |err| switch (err) {
- error.KeyNotFound => return false,
- };
- defer options_key.closeKey();
-
- const option_name = comptime switch (builtin.target.cpu.arch) {
- .arm, .armeb => "OptionId.DesktopCPParm",
- .aarch64 => "OptionId.DesktopCPParm64",
- .x86_64 => "OptionId.DesktopCPPx64",
- .x86 => "OptionId.DesktopCPPx86",
- else => |tag| @compileError("Windows 10 SDK cannot be detected on architecture " ++ tag),
- };
-
- const reg_value = options_key.getDword("", option_name) catch return false;
- return (reg_value == 1);
- }
-
- fn free(self: *const Windows10Sdk, allocator: std.mem.Allocator) void {
- allocator.free(self.path);
- allocator.free(self.version);
- }
-};
-
-pub const Windows81Sdk = struct {
- path: []const u8,
- version: []const u8,
-
- /// Find path and version of Windows 8.1 SDK.
- /// Caller owns the result's fields.
- /// After finishing work, call `free(allocator)`.
- fn find(allocator: std.mem.Allocator, roots_key: *const RegistryWtf8) error{ OutOfMemory, Windows81SdkNotFound, PathTooLong, VersionTooLong }!Windows81Sdk {
- const path: []const u8 = path81: {
- const path_maybe_with_trailing_slash = roots_key.getString(allocator, "", "KitsRoot81") catch |err| switch (err) {
- error.NotAString => return error.Windows81SdkNotFound,
- error.ValueNameNotFound => return error.Windows81SdkNotFound,
- error.StringNotFound => return error.Windows81SdkNotFound,
-
- error.OutOfMemory => return error.OutOfMemory,
- };
- if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
- allocator.free(path_maybe_with_trailing_slash);
- return error.PathTooLong;
- }
-
- var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
- errdefer path.deinit();
-
- // String might contain trailing slash, so trim it here
- if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
-
- const path_without_trailing_slash = try path.toOwnedSlice();
- break :path81 path_without_trailing_slash;
- };
- errdefer allocator.free(path);
-
- const version: []const u8 = version81: {
- var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
- const sdk_lib_dir_path = std.fmt.bufPrint(buf[0..], "{s}\\Lib\\", .{path}) catch |err| switch (err) {
- error.NoSpaceLeft => return error.PathTooLong,
- };
- if (!std.fs.path.isAbsolute(sdk_lib_dir_path)) return error.Windows81SdkNotFound;
-
- // enumerate files in sdk path looking for latest version
- var sdk_lib_dir = std.fs.openDirAbsolute(sdk_lib_dir_path, .{
- .iterate = true,
- }) catch |err| switch (err) {
- error.NameTooLong => return error.PathTooLong,
- else => return error.Windows81SdkNotFound,
- };
- defer sdk_lib_dir.close();
-
- var iterator = sdk_lib_dir.iterate();
- const versions = iterateAndFilterBySemVer(&iterator, allocator, "winv") catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.VersionNotFound => return error.Windows81SdkNotFound,
- };
- defer {
- for (versions) |version| allocator.free(version);
- allocator.free(versions);
- }
- const latest_version = try allocator.dupe(u8, versions[0]);
- break :version81 latest_version;
- };
- errdefer allocator.free(version);
-
- return Windows81Sdk{ .path = path, .version = version };
- }
-
- fn free(self: *const Windows81Sdk, allocator: std.mem.Allocator) void {
- allocator.free(self.path);
- allocator.free(self.version);
- }
-};
-
-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}";
- const setup_config_key = RegistryWtf8.openKey(windows.HKEY_CLASSES_ROOT, "CLSID\\" ++ setup_configuration_clsid) catch |err| switch (err) {
- error.KeyNotFound => return error.PathNotFound,
- };
- defer setup_config_key.closeKey();
-
- const dll_path = setup_config_key.getString(allocator, "InprocServer32", "") catch |err| switch (err) {
- error.NotAString,
- error.ValueNameNotFound,
- error.StringNotFound,
- => return error.PathNotFound,
-
- error.OutOfMemory => return error.OutOfMemory,
- };
- defer allocator.free(dll_path);
-
- var path_it = std.fs.path.componentIterator(dll_path) catch return error.PathNotFound;
- // the .dll filename
- _ = path_it.last();
- const root_path = while (path_it.previous()) |dir_component| {
- if (std.ascii.eqlIgnoreCase(dir_component.name, "VisualStudio")) {
- break dir_component.path;
- }
- } else {
- return error.PathNotFound;
- };
-
- const instances_path = try std.fs.path.join(allocator, &.{ root_path, "Packages", "_Instances" });
- defer allocator.free(instances_path);
-
- return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return error.PathNotFound;
- }
-
- fn findInstancesDir(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {
- // First try to get the path from the .dll that would have been
- // loaded via COM for SetupConfiguration.
- return findInstancesDirViaCLSID(allocator) catch |orig_err| {
- // If that can't be found, fall back to manually appending
- // `Microsoft\VisualStudio\Packages\_Instances` to %PROGRAMDATA%
- const program_data = std.process.getEnvVarOwned(allocator, "PROGRAMDATA") catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- else => return orig_err,
- };
- defer allocator.free(program_data);
-
- const instances_path = try std.fs.path.join(allocator, &.{ program_data, "Microsoft", "VisualStudio", "Packages", "_Instances" });
- defer allocator.free(instances_path);
-
- return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return orig_err;
- };
- }
-
- /// Intended to be equivalent to `ISetupHelper.ParseVersion`
- /// Example: 17.4.33205.214 -> 0x0011000481b500d6
- fn parseVersionQuad(version: []const u8) error{InvalidVersion}!u64 {
- var it = std.mem.splitScalar(u8, version, '.');
- const a = it.next() orelse return error.InvalidVersion;
- const b = it.next() orelse return error.InvalidVersion;
- const c = it.next() orelse return error.InvalidVersion;
- const d = it.next() orelse return error.InvalidVersion;
- if (it.next()) |_| return error.InvalidVersion;
- var result: u64 = undefined;
- var result_bytes = std.mem.asBytes(&result);
-
- std.mem.writeInt(
- u16,
- result_bytes[0..2],
- std.fmt.parseUnsigned(u16, d, 10) catch return error.InvalidVersion,
- .little,
- );
- std.mem.writeInt(
- u16,
- result_bytes[2..4],
- std.fmt.parseUnsigned(u16, c, 10) catch return error.InvalidVersion,
- .little,
- );
- std.mem.writeInt(
- u16,
- result_bytes[4..6],
- std.fmt.parseUnsigned(u16, b, 10) catch return error.InvalidVersion,
- .little,
- );
- std.mem.writeInt(
- u16,
- result_bytes[6..8],
- std.fmt.parseUnsigned(u16, a, 10) catch return error.InvalidVersion,
- .little,
- );
-
- return result;
- }
-
- /// Intended to be equivalent to ISetupConfiguration.EnumInstances:
- /// https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.setup.configuration
- /// but without the use of COM in order to avoid a dependency on ole32.dll
- ///
- /// The logic in this function is intended to match what ISetupConfiguration does
- /// under-the-hood, as verified using Procmon.
- fn findViaCOM(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
- // Typically `%PROGRAMDATA%\Microsoft\VisualStudio\Packages\_Instances`
- // This will contain directories with names of instance IDs like 80a758ca,
- // which will contain `state.json` files that have the version and
- // installation directory.
- var instances_dir = try findInstancesDir(allocator);
- defer instances_dir.close();
-
- var state_subpath_buf: [std.fs.MAX_NAME_BYTES + 32]u8 = undefined;
- var latest_version_lib_dir = std.ArrayListUnmanaged(u8){};
- errdefer latest_version_lib_dir.deinit(allocator);
-
- var latest_version: u64 = 0;
- var instances_dir_it = instances_dir.iterateAssumeFirstIteration();
- while (instances_dir_it.next() catch return error.PathNotFound) |entry| {
- if (entry.kind != .directory) continue;
-
- var fbs = std.io.fixedBufferStream(&state_subpath_buf);
- const writer = fbs.writer();
-
- writer.writeAll(entry.name) catch unreachable;
- writer.writeByte(std.fs.path.sep) catch unreachable;
- writer.writeAll("state.json") catch unreachable;
-
- const json_contents = instances_dir.readFileAlloc(allocator, fbs.getWritten(), std.math.maxInt(usize)) catch continue;
- defer allocator.free(json_contents);
-
- var parsed = std.json.parseFromSlice(std.json.Value, allocator, json_contents, .{}) catch continue;
- defer parsed.deinit();
-
- if (parsed.value != .object) continue;
- const catalog_info = parsed.value.object.get("catalogInfo") orelse continue;
- if (catalog_info != .object) continue;
- const product_version_value = catalog_info.object.get("buildVersion") orelse continue;
- if (product_version_value != .string) continue;
- const product_version_text = product_version_value.string;
- const parsed_version = parseVersionQuad(product_version_text) catch continue;
-
- // We want to end up with the most recent version installed
- if (parsed_version <= latest_version) continue;
-
- const installation_path = parsed.value.object.get("installationPath") orelse continue;
- if (installation_path != .string) continue;
-
- const lib_dir_path = libDirFromInstallationPath(allocator, installation_path.string) catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- error.PathNotFound => continue,
- };
- defer allocator.free(lib_dir_path);
-
- latest_version_lib_dir.clearRetainingCapacity();
- try latest_version_lib_dir.appendSlice(allocator, lib_dir_path);
- latest_version = parsed_version;
- }
-
- if (latest_version_lib_dir.items.len == 0) return error.PathNotFound;
- return latest_version_lib_dir.toOwnedSlice(allocator);
- }
-
- fn libDirFromInstallationPath(allocator: std.mem.Allocator, installation_path: []const u8) error{ OutOfMemory, PathNotFound }![]const u8 {
- var lib_dir_buf = try std.ArrayList(u8).initCapacity(allocator, installation_path.len + 64);
- errdefer lib_dir_buf.deinit();
-
- lib_dir_buf.appendSliceAssumeCapacity(installation_path);
-
- if (!std.fs.path.isSep(lib_dir_buf.getLast())) {
- try lib_dir_buf.append('\\');
- }
- const installation_path_with_trailing_sep_len = lib_dir_buf.items.len;
-
- try lib_dir_buf.appendSlice("VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
- var default_tools_version_buf: [512]u8 = undefined;
- const default_tools_version_contents = std.fs.cwd().readFile(lib_dir_buf.items, &default_tools_version_buf) catch {
- return error.PathNotFound;
- };
- var tokenizer = std.mem.tokenizeAny(u8, default_tools_version_contents, " \r\n");
- const default_tools_version = tokenizer.next() orelse return error.PathNotFound;
-
- lib_dir_buf.shrinkRetainingCapacity(installation_path_with_trailing_sep_len);
- try lib_dir_buf.appendSlice("VC\\Tools\\MSVC\\");
- try lib_dir_buf.appendSlice(default_tools_version);
- const folder_with_arch = "\\Lib\\" ++ comptime switch (builtin.target.cpu.arch) {
- .x86 => "x86",
- .x86_64 => "x64",
- .arm, .armeb => "arm",
- .aarch64 => "arm64",
- else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag),
- };
- try lib_dir_buf.appendSlice(folder_with_arch);
-
- if (!verifyLibDir(lib_dir_buf.items)) {
- return error.PathNotFound;
- }
-
- return lib_dir_buf.toOwnedSlice();
- }
-
- // https://learn.microsoft.com/en-us/visualstudio/install/tools-for-managing-visual-studio-instances?view=vs-2022#editing-the-registry-for-a-visual-studio-instance
- fn findViaRegistry(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
-
- // %localappdata%\Microsoft\VisualStudio\
- // %appdata%\Local\Microsoft\VisualStudio\
- const visualstudio_folder_path = std.fs.getAppDataDir(allocator, "Microsoft\\VisualStudio\\") catch return error.PathNotFound;
- defer allocator.free(visualstudio_folder_path);
-
- const vs_versions: []const []const u8 = vs_versions: {
- if (!std.fs.path.isAbsolute(visualstudio_folder_path)) return error.PathNotFound;
- // enumerate folders that contain `privateregistry.bin`, looking for all versions
- // f.i. %localappdata%\Microsoft\VisualStudio\17.0_9e9cbb98\
- var visualstudio_folder = std.fs.openDirAbsolute(visualstudio_folder_path, .{
- .iterate = true,
- }) catch return error.PathNotFound;
- defer visualstudio_folder.close();
-
- var iterator = visualstudio_folder.iterate();
- const versions = iterateAndFilterBySemVer(&iterator, allocator, null) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.VersionNotFound => return error.PathNotFound,
- };
- break :vs_versions versions;
- };
- defer {
- for (vs_versions) |vs_version| allocator.free(vs_version);
- allocator.free(vs_versions);
- }
- var config_subkey_buf: [RegistryWtf16Le.key_name_max_len * 2]u8 = undefined;
- const source_directories: []const u8 = source_directories: for (vs_versions) |vs_version| {
- const privateregistry_absolute_path = std.fs.path.join(allocator, &.{ visualstudio_folder_path, vs_version, "privateregistry.bin" }) catch continue;
- defer allocator.free(privateregistry_absolute_path);
- if (!std.fs.path.isAbsolute(privateregistry_absolute_path)) continue;
-
- const visualstudio_registry = RegistryWtf8.loadFromPath(privateregistry_absolute_path) catch continue;
- defer visualstudio_registry.closeKey();
-
- const config_subkey = std.fmt.bufPrint(config_subkey_buf[0..], "Software\\Microsoft\\VisualStudio\\{s}_Config", .{vs_version}) catch unreachable;
-
- const source_directories_value = visualstudio_registry.getString(allocator, config_subkey, "Source Directories") catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => continue,
- };
- if (source_directories_value.len > (std.fs.MAX_PATH_BYTES * 30)) { // note(bratishkaerik): guessing from the fact that on my computer it has 15 pathes and at least some of them are not of max length
- allocator.free(source_directories_value);
- continue;
- }
-
- break :source_directories source_directories_value;
- } else return error.PathNotFound;
- defer allocator.free(source_directories);
-
- var source_directories_splitted = std.mem.splitScalar(u8, source_directories, ';');
-
- const msvc_dir: []const u8 = msvc_dir: {
- const msvc_include_dir_maybe_with_trailing_slash = try allocator.dupe(u8, source_directories_splitted.first());
-
- if (msvc_include_dir_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(msvc_include_dir_maybe_with_trailing_slash)) {
- allocator.free(msvc_include_dir_maybe_with_trailing_slash);
- return error.PathNotFound;
- }
-
- var msvc_dir = std.ArrayList(u8).fromOwnedSlice(allocator, msvc_include_dir_maybe_with_trailing_slash);
- errdefer msvc_dir.deinit();
-
- // String might contain trailing slash, so trim it here
- if (msvc_dir.items.len > "C:\\".len and msvc_dir.getLast() == '\\') _ = msvc_dir.pop();
-
- // Remove `\include` at the end of path
- if (std.mem.endsWith(u8, msvc_dir.items, "\\include")) {
- msvc_dir.shrinkRetainingCapacity(msvc_dir.items.len - "\\include".len);
- }
-
- const folder_with_arch = "\\Lib\\" ++ comptime switch (builtin.target.cpu.arch) {
- .x86 => "x86",
- .x86_64 => "x64",
- .arm, .armeb => "arm",
- .aarch64 => "arm64",
- else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag),
- };
-
- try msvc_dir.appendSlice(folder_with_arch);
- const msvc_dir_with_arch = try msvc_dir.toOwnedSlice();
- break :msvc_dir msvc_dir_with_arch;
- };
- errdefer allocator.free(msvc_dir);
-
- if (!verifyLibDir(msvc_dir)) {
- return error.PathNotFound;
- }
-
- return msvc_dir;
- }
-
- fn findViaVs7Key(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
- var base_path: std.ArrayList(u8) = base_path: {
- try_env: {
- var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => break :try_env,
- };
- defer env_map.deinit();
-
- if (env_map.get("VS140COMNTOOLS")) |VS140COMNTOOLS| {
- if (VS140COMNTOOLS.len < "C:\\Common7\\Tools".len) break :try_env;
- if (!std.fs.path.isAbsolute(VS140COMNTOOLS)) break :try_env;
- var list = std.ArrayList(u8).init(allocator);
- errdefer list.deinit();
-
- try list.appendSlice(VS140COMNTOOLS); // C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools
- // String might contain trailing slash, so trim it here
- if (list.items.len > "C:\\".len and list.getLast() == '\\') _ = list.pop();
- list.shrinkRetainingCapacity(list.items.len - "\\Common7\\Tools".len); // C:\Program Files (x86)\Microsoft Visual Studio 14.0
- break :base_path list;
- }
- }
-
- const vs7_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7") catch return error.PathNotFound;
- defer vs7_key.closeKey();
- try_vs7_key: {
- const path_maybe_with_trailing_slash = vs7_key.getString(allocator, "", "14.0") catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => break :try_vs7_key,
- };
-
- if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
- allocator.free(path_maybe_with_trailing_slash);
- break :try_vs7_key;
- }
-
- var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
- errdefer path.deinit();
-
- // String might contain trailing slash, so trim it here
- if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
- break :base_path path;
- }
- return error.PathNotFound;
- };
- errdefer base_path.deinit();
-
- const folder_with_arch = "\\VC\\lib\\" ++ comptime switch (builtin.target.cpu.arch) {
- .x86 => "", //x86 is in the root of the Lib folder
- .x86_64 => "amd64",
- .arm, .armeb => "arm",
- .aarch64 => "arm64",
- else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag),
- };
- try base_path.appendSlice(folder_with_arch);
-
- if (!verifyLibDir(base_path.items)) {
- return error.PathNotFound;
- }
-
- const full_path = try base_path.toOwnedSlice();
- return full_path;
- }
-
- fn verifyLibDir(lib_dir_path: []const u8) bool {
- std.debug.assert(std.fs.path.isAbsolute(lib_dir_path)); // should be already handled in `findVia*`
-
- var dir = std.fs.openDirAbsolute(lib_dir_path, .{}) catch return false;
- defer dir.close();
-
- const stat = dir.statFile("vcruntime.lib") catch return false;
- if (stat.kind != .file)
- return false;
-
- return true;
- }
-
- /// Find path to MSVC's `lib/` directory.
- /// Caller owns the result.
- pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, MsvcLibDirNotFound }![]const u8 {
- const full_path = MsvcLibDir.findViaCOM(allocator) catch |err1| switch (err1) {
- error.OutOfMemory => return error.OutOfMemory,
- error.PathNotFound => MsvcLibDir.findViaRegistry(allocator) catch |err2| switch (err2) {
- error.OutOfMemory => return error.OutOfMemory,
- error.PathNotFound => MsvcLibDir.findViaVs7Key(allocator) catch |err3| switch (err3) {
- error.OutOfMemory => return error.OutOfMemory,
- error.PathNotFound => return error.MsvcLibDirNotFound,
- },
- },
- };
- errdefer allocator.free(full_path);
-
- return full_path;
- }
-};