authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-28 11:34:45-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-02-28 11:34:45-08:00
log9410b11ca663231e367e3adfd668979c4b870a41
tree54f258bb3a122f37270d3a2731265b9cb0b3c9e1
parenta1b083b6667bbacc80db485ea97eceb6867cf600
parentdff45c266e6c6103ab324c4771a21d5b52ef85ba
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #19114 from ziglang/lazy-resinator

move `zig libc` command to be lazily built

22 files changed, 2403 insertions(+), 2380 deletions(-)

CMakeLists.txt+5-5
......@@ -507,16 +507,18 @@ set(ZIG_STAGE2_SOURCES
507507 "${CMAKE_SOURCE_DIR}/lib/std/zig/Ast.zig"
508508 "${CMAKE_SOURCE_DIR}/lib/std/zig/AstGen.zig"
509509 "${CMAKE_SOURCE_DIR}/lib/std/zig/AstRlAnnotate.zig"
510 "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig"
510 "${CMAKE_SOURCE_DIR}/lib/std/zig/LibCInstallation.zig"
511511 "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig"
512 "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig"
513512 "${CMAKE_SOURCE_DIR}/lib/std/zig/Server.zig"
513 "${CMAKE_SOURCE_DIR}/lib/std/zig/WindowsSdk.zig"
514 "${CMAKE_SOURCE_DIR}/lib/std/zig/Zir.zig"
515 "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig"
516 "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig"
514517 "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig"
515518 "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"
516519 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig"
517520 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/x86.zig"
518521 "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig"
519 "${CMAKE_SOURCE_DIR}/lib/std/zig/Zir.zig"
520522 "${CMAKE_SOURCE_DIR}/src/Air.zig"
521523 "${CMAKE_SOURCE_DIR}/src/Compilation.zig"
522524 "${CMAKE_SOURCE_DIR}/src/Compilation/Config.zig"
......@@ -570,7 +572,6 @@ set(ZIG_STAGE2_SOURCES
570572 "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig"
571573 "${CMAKE_SOURCE_DIR}/src/glibc.zig"
572574 "${CMAKE_SOURCE_DIR}/src/introspect.zig"
573 "${CMAKE_SOURCE_DIR}/src/libc_installation.zig"
574575 "${CMAKE_SOURCE_DIR}/src/libcxx.zig"
575576 "${CMAKE_SOURCE_DIR}/src/libtsan.zig"
576577 "${CMAKE_SOURCE_DIR}/src/libunwind.zig"
......@@ -645,7 +646,6 @@ set(ZIG_STAGE2_SOURCES
645646 "${CMAKE_SOURCE_DIR}/src/translate_c/ast.zig"
646647 "${CMAKE_SOURCE_DIR}/src/type.zig"
647648 "${CMAKE_SOURCE_DIR}/src/wasi_libc.zig"
648 "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig"
649649 "${CMAKE_SOURCE_DIR}/src/stubs/aro_builtins.zig"
650650 "${CMAKE_SOURCE_DIR}/src/stubs/aro_names.zig"
651651)
lib/compiler/libc.zig created+137
......@@ -0,0 +1,137 @@
1const std = @import("std");
2const mem = std.mem;
3const io = std.io;
4const LibCInstallation = std.zig.LibCInstallation;
5
6const usage_libc =
7 \\Usage: zig libc
8 \\
9 \\ Detect the native libc installation and print the resulting
10 \\ paths to stdout. You can save this into a file and then edit
11 \\ the paths to create a cross compilation libc kit. Then you
12 \\ can pass `--libc [file]` for Zig to use it.
13 \\
14 \\Usage: zig libc [paths_file]
15 \\
16 \\ Parse a libc installation text file and validate it.
17 \\
18 \\Options:
19 \\ -h, --help Print this help and exit
20 \\ -target [name] <arch><sub>-<os>-<abi> see the targets command
21 \\ -includes Print the libc include directories for the target
22 \\
23;
24
25pub fn main() !void {
26 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
27 defer arena_instance.deinit();
28 const arena = arena_instance.allocator();
29 const gpa = arena;
30
31 const args = try std.process.argsAlloc(arena);
32 const zig_lib_directory = args[1];
33
34 var input_file: ?[]const u8 = null;
35 var target_arch_os_abi: []const u8 = "native";
36 var print_includes: bool = false;
37 {
38 var i: usize = 2;
39 while (i < args.len) : (i += 1) {
40 const arg = args[i];
41 if (mem.startsWith(u8, arg, "-")) {
42 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
43 const stdout = std.io.getStdOut().writer();
44 try stdout.writeAll(usage_libc);
45 return std.process.cleanExit();
46 } else if (mem.eql(u8, arg, "-target")) {
47 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
48 i += 1;
49 target_arch_os_abi = args[i];
50 } else if (mem.eql(u8, arg, "-includes")) {
51 print_includes = true;
52 } else {
53 fatal("unrecognized parameter: '{s}'", .{arg});
54 }
55 } else if (input_file != null) {
56 fatal("unexpected extra parameter: '{s}'", .{arg});
57 } else {
58 input_file = arg;
59 }
60 }
61 }
62
63 const target_query = std.zig.parseTargetQueryOrReportFatalError(gpa, .{
64 .arch_os_abi = target_arch_os_abi,
65 });
66 const target = std.zig.resolveTargetQueryOrFatal(target_query);
67
68 if (print_includes) {
69 const libc_installation: ?*LibCInstallation = libc: {
70 if (input_file) |libc_file| {
71 const libc = try arena.create(LibCInstallation);
72 libc.* = LibCInstallation.parse(arena, libc_file, target) catch |err| {
73 fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) });
74 };
75 break :libc libc;
76 } else {
77 break :libc null;
78 }
79 };
80
81 const is_native_abi = target_query.isNativeAbi();
82
83 const libc_dirs = std.zig.LibCDirs.detect(
84 arena,
85 zig_lib_directory,
86 target,
87 is_native_abi,
88 true,
89 libc_installation,
90 ) catch |err| {
91 const zig_target = try target.zigTriple(arena);
92 fatal("unable to detect libc for target {s}: {s}", .{ zig_target, @errorName(err) });
93 };
94
95 if (libc_dirs.libc_include_dir_list.len == 0) {
96 const zig_target = try target.zigTriple(arena);
97 fatal("no include dirs detected for target {s}", .{zig_target});
98 }
99
100 var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
101 var writer = bw.writer();
102 for (libc_dirs.libc_include_dir_list) |include_dir| {
103 try writer.writeAll(include_dir);
104 try writer.writeByte('\n');
105 }
106 try bw.flush();
107 return std.process.cleanExit();
108 }
109
110 if (input_file) |libc_file| {
111 var libc = LibCInstallation.parse(gpa, libc_file, target) catch |err| {
112 fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) });
113 };
114 defer libc.deinit(gpa);
115 } else {
116 if (!target_query.isNative()) {
117 fatal("unable to detect libc for non-native target", .{});
118 }
119 var libc = LibCInstallation.findNative(.{
120 .allocator = gpa,
121 .verbose = true,
122 .target = target,
123 }) catch |err| {
124 fatal("unable to detect native libc: {s}", .{@errorName(err)});
125 };
126 defer libc.deinit(gpa);
127
128 var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
129 try libc.render(bw.writer());
130 try bw.flush();
131 }
132}
133
134fn fatal(comptime format: []const u8, args: anytype) noreturn {
135 std.log.err(format, args);
136 std.process.exit(1);
137}
lib/std/Target.zig+16
......@@ -2758,6 +2758,22 @@ fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool {
27582758 }
27592759}
27602760
2761pub fn osArchName(target: std.Target) [:0]const u8 {
2762 return switch (target.os.tag) {
2763 .linux => switch (target.cpu.arch) {
2764 .arm, .armeb, .thumb, .thumbeb => "arm",
2765 .aarch64, .aarch64_be, .aarch64_32 => "aarch64",
2766 .mips, .mipsel, .mips64, .mips64el => "mips",
2767 .powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc",
2768 .riscv32, .riscv64 => "riscv",
2769 .sparc, .sparcel, .sparc64 => "sparc",
2770 .x86, .x86_64 => "x86",
2771 else => @tagName(target.cpu.arch),
2772 },
2773 else => @tagName(target.cpu.arch),
2774 };
2775}
2776
27612777const Target = @This();
27622778const std = @import("std.zig");
27632779const builtin = @import("builtin");
lib/std/zig.zig+119-13
......@@ -14,6 +14,10 @@ pub const system = @import("zig/system.zig");
1414pub const CrossTarget = std.Target.Query;
1515pub const BuiltinFn = @import("zig/BuiltinFn.zig");
1616pub const AstRlAnnotate = @import("zig/AstRlAnnotate.zig");
17pub const LibCInstallation = @import("zig/LibCInstallation.zig");
18pub const WindowsSdk = @import("zig/WindowsSdk.zig");
19pub const LibCDirs = @import("zig/LibCDirs.zig");
20pub const target = @import("zig/target.zig");
1721
1822// Character literal parsing
1923pub const ParsedCharLiteral = string_literal.ParsedCharLiteral;
......@@ -142,10 +146,10 @@ pub const BinNameOptions = struct {
142146/// Returns the standard file system basename of a binary generated by the Zig compiler.
143147pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 {
144148 const root_name = options.root_name;
145 const target = options.target;
146 switch (target.ofmt) {
149 const t = options.target;
150 switch (t.ofmt) {
147151 .coff => switch (options.output_mode) {
148 .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
152 .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, t.exeFileExt() }),
149153 .Lib => {
150154 const suffix = switch (options.link_mode orelse .Static) {
151155 .Static => ".lib",
......@@ -160,16 +164,16 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe
160164 .Lib => {
161165 switch (options.link_mode orelse .Static) {
162166 .Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
163 target.libPrefix(), root_name,
167 t.libPrefix(), root_name,
164168 }),
165169 .Dynamic => {
166170 if (options.version) |ver| {
167171 return std.fmt.allocPrint(allocator, "{s}{s}.so.{d}.{d}.{d}", .{
168 target.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
172 t.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
169173 });
170174 } else {
171175 return std.fmt.allocPrint(allocator, "{s}{s}.so", .{
172 target.libPrefix(), root_name,
176 t.libPrefix(), root_name,
173177 });
174178 }
175179 },
......@@ -182,16 +186,16 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe
182186 .Lib => {
183187 switch (options.link_mode orelse .Static) {
184188 .Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
185 target.libPrefix(), root_name,
189 t.libPrefix(), root_name,
186190 }),
187191 .Dynamic => {
188192 if (options.version) |ver| {
189193 return std.fmt.allocPrint(allocator, "{s}{s}.{d}.{d}.{d}.dylib", .{
190 target.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
194 t.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
191195 });
192196 } else {
193197 return std.fmt.allocPrint(allocator, "{s}{s}.dylib", .{
194 target.libPrefix(), root_name,
198 t.libPrefix(), root_name,
195199 });
196200 }
197201 },
......@@ -200,11 +204,11 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe
200204 .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
201205 },
202206 .wasm => switch (options.output_mode) {
203 .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
207 .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, t.exeFileExt() }),
204208 .Lib => {
205209 switch (options.link_mode orelse .Static) {
206210 .Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
207 target.libPrefix(), root_name,
211 t.libPrefix(), root_name,
208212 }),
209213 .Dynamic => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}),
210214 }
......@@ -218,10 +222,10 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe
218222 .plan9 => switch (options.output_mode) {
219223 .Exe => return allocator.dupe(u8, root_name),
220224 .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{
221 root_name, target.ofmt.fileExt(target.cpu.arch),
225 root_name, t.ofmt.fileExt(t.cpu.arch),
222226 }),
223227 .Lib => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
224 target.libPrefix(), root_name,
228 t.libPrefix(), root_name,
225229 }),
226230 },
227231 .nvptx => return std.fmt.allocPrint(allocator, "{s}.ptx", .{root_name}),
......@@ -900,15 +904,117 @@ pub fn putAstErrorsIntoBundle(
900904 try wip_errors.addZirErrorMessages(zir, tree, tree.source, path);
901905}
902906
907pub fn resolveTargetQueryOrFatal(target_query: std.Target.Query) std.Target {
908 return std.zig.system.resolveTargetQuery(target_query) catch |err|
909 fatal("unable to resolve target: {s}", .{@errorName(err)});
910}
911
912pub fn parseTargetQueryOrReportFatalError(
913 allocator: Allocator,
914 opts: std.Target.Query.ParseOptions,
915) std.Target.Query {
916 var opts_with_diags = opts;
917 var diags: std.Target.Query.ParseOptions.Diagnostics = .{};
918 if (opts_with_diags.diagnostics == null) {
919 opts_with_diags.diagnostics = &diags;
920 }
921 return std.Target.Query.parse(opts_with_diags) catch |err| switch (err) {
922 error.UnknownCpuModel => {
923 help: {
924 var help_text = std.ArrayList(u8).init(allocator);
925 defer help_text.deinit();
926 for (diags.arch.?.allCpuModels()) |cpu| {
927 help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help;
928 }
929 std.log.info("available CPUs for architecture '{s}':\n{s}", .{
930 @tagName(diags.arch.?), help_text.items,
931 });
932 }
933 fatal("unknown CPU: '{s}'", .{diags.cpu_name.?});
934 },
935 error.UnknownCpuFeature => {
936 help: {
937 var help_text = std.ArrayList(u8).init(allocator);
938 defer help_text.deinit();
939 for (diags.arch.?.allFeaturesList()) |feature| {
940 help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help;
941 }
942 std.log.info("available CPU features for architecture '{s}':\n{s}", .{
943 @tagName(diags.arch.?), help_text.items,
944 });
945 }
946 fatal("unknown CPU feature: '{s}'", .{diags.unknown_feature_name.?});
947 },
948 error.UnknownObjectFormat => {
949 help: {
950 var help_text = std.ArrayList(u8).init(allocator);
951 defer help_text.deinit();
952 inline for (@typeInfo(std.Target.ObjectFormat).Enum.fields) |field| {
953 help_text.writer().print(" {s}\n", .{field.name}) catch break :help;
954 }
955 std.log.info("available object formats:\n{s}", .{help_text.items});
956 }
957 fatal("unknown object format: '{s}'", .{opts.object_format.?});
958 },
959 else => |e| fatal("unable to parse target query '{s}': {s}", .{
960 opts.arch_os_abi, @errorName(e),
961 }),
962 };
963}
964
965pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
966 std.log.err(format, args);
967 std.process.exit(1);
968}
969
970/// Collects all the environment variables that Zig could possibly inspect, so
971/// that we can do reflection on this and print them with `zig env`.
972pub const EnvVar = enum {
973 ZIG_GLOBAL_CACHE_DIR,
974 ZIG_LOCAL_CACHE_DIR,
975 ZIG_LIB_DIR,
976 ZIG_LIBC,
977 ZIG_BUILD_RUNNER,
978 ZIG_VERBOSE_LINK,
979 ZIG_VERBOSE_CC,
980 ZIG_BTRFS_WORKAROUND,
981 ZIG_DEBUG_CMD,
982 CC,
983 NO_COLOR,
984 XDG_CACHE_HOME,
985 HOME,
986
987 pub fn isSet(comptime ev: EnvVar) bool {
988 return std.process.hasEnvVarConstant(@tagName(ev));
989 }
990
991 pub fn get(ev: EnvVar, arena: std.mem.Allocator) !?[]u8 {
992 if (std.process.getEnvVarOwned(arena, @tagName(ev))) |value| {
993 return value;
994 } else |err| switch (err) {
995 error.EnvironmentVariableNotFound => return null,
996 else => |e| return e,
997 }
998 }
999
1000 pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 {
1001 return std.os.getenvZ(@tagName(ev));
1002 }
1003};
1004
9031005test {
9041006 _ = Ast;
9051007 _ = AstRlAnnotate;
9061008 _ = BuiltinFn;
9071009 _ = Client;
9081010 _ = ErrorBundle;
1011 _ = LibCDirs;
1012 _ = LibCInstallation;
9091013 _ = Server;
1014 _ = WindowsSdk;
9101015 _ = number_literal;
9111016 _ = primitives;
9121017 _ = string_literal;
9131018 _ = system;
1019 _ = target;
9141020}
lib/std/zig/LibCDirs.zig created+281
......@@ -0,0 +1,281 @@
1libc_include_dir_list: []const []const u8,
2libc_installation: ?*const LibCInstallation,
3libc_framework_dir_list: []const []const u8,
4sysroot: ?[]const u8,
5darwin_sdk_layout: ?DarwinSdkLayout,
6
7/// The filesystem layout of darwin SDK elements.
8pub const DarwinSdkLayout = enum {
9 /// macOS SDK layout: TOP { /usr/include, /usr/lib, /System/Library/Frameworks }.
10 sdk,
11 /// Shipped libc layout: TOP { /lib/libc/include, /lib/libc/darwin, <NONE> }.
12 vendored,
13};
14
15pub fn detect(
16 arena: Allocator,
17 zig_lib_dir: []const u8,
18 target: std.Target,
19 is_native_abi: bool,
20 link_libc: bool,
21 libc_installation: ?*const LibCInstallation,
22) !LibCDirs {
23 if (!link_libc) {
24 return .{
25 .libc_include_dir_list = &[0][]u8{},
26 .libc_installation = null,
27 .libc_framework_dir_list = &.{},
28 .sysroot = null,
29 .darwin_sdk_layout = null,
30 };
31 }
32
33 if (libc_installation) |lci| {
34 return detectFromInstallation(arena, target, lci);
35 }
36
37 // If linking system libraries and targeting the native abi, default to
38 // using the system libc installation.
39 if (is_native_abi and !target.isMinGW()) {
40 const libc = try arena.create(LibCInstallation);
41 libc.* = LibCInstallation.findNative(.{ .allocator = arena, .target = target }) catch |err| switch (err) {
42 error.CCompilerExitCode,
43 error.CCompilerCrashed,
44 error.CCompilerCannotFindHeaders,
45 error.UnableToSpawnCCompiler,
46 error.DarwinSdkNotFound,
47 => |e| {
48 // We tried to integrate with the native system C compiler,
49 // however, it is not installed. So we must rely on our bundled
50 // libc files.
51 if (std.zig.target.canBuildLibC(target)) {
52 return detectFromBuilding(arena, zig_lib_dir, target);
53 }
54 return e;
55 },
56 else => |e| return e,
57 };
58 return detectFromInstallation(arena, target, libc);
59 }
60
61 // If not linking system libraries, build and provide our own libc by
62 // default if possible.
63 if (std.zig.target.canBuildLibC(target)) {
64 return detectFromBuilding(arena, zig_lib_dir, target);
65 }
66
67 // If zig can't build the libc for the target and we are targeting the
68 // native abi, fall back to using the system libc installation.
69 // On windows, instead of the native (mingw) abi, we want to check
70 // for the MSVC abi as a fallback.
71 const use_system_abi = if (builtin.os.tag == .windows)
72 target.abi == .msvc
73 else
74 is_native_abi;
75
76 if (use_system_abi) {
77 const libc = try arena.create(LibCInstallation);
78 libc.* = try LibCInstallation.findNative(.{ .allocator = arena, .verbose = true, .target = target });
79 return detectFromInstallation(arena, target, libc);
80 }
81
82 return .{
83 .libc_include_dir_list = &[0][]u8{},
84 .libc_installation = null,
85 .libc_framework_dir_list = &.{},
86 .sysroot = null,
87 .darwin_sdk_layout = null,
88 };
89}
90
91fn detectFromInstallation(arena: Allocator, target: std.Target, lci: *const LibCInstallation) !LibCDirs {
92 var list = try std.ArrayList([]const u8).initCapacity(arena, 5);
93 var framework_list = std.ArrayList([]const u8).init(arena);
94
95 list.appendAssumeCapacity(lci.include_dir.?);
96
97 const is_redundant = std.mem.eql(u8, lci.sys_include_dir.?, lci.include_dir.?);
98 if (!is_redundant) list.appendAssumeCapacity(lci.sys_include_dir.?);
99
100 if (target.os.tag == .windows) {
101 if (std.fs.path.dirname(lci.sys_include_dir.?)) |sys_include_dir_parent| {
102 // This include path will only exist when the optional "Desktop development with C++"
103 // is installed. It contains headers, .rc files, and resources. It is especially
104 // necessary when working with Windows resources.
105 const atlmfc_dir = try std.fs.path.join(arena, &[_][]const u8{ sys_include_dir_parent, "atlmfc", "include" });
106 list.appendAssumeCapacity(atlmfc_dir);
107 }
108 if (std.fs.path.dirname(lci.include_dir.?)) |include_dir_parent| {
109 const um_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "um" });
110 list.appendAssumeCapacity(um_dir);
111
112 const shared_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "shared" });
113 list.appendAssumeCapacity(shared_dir);
114 }
115 }
116 if (target.os.tag == .haiku) {
117 const include_dir_path = lci.include_dir orelse return error.LibCInstallationNotAvailable;
118 const os_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os" });
119 list.appendAssumeCapacity(os_dir);
120 // Errors.h
121 const os_support_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os/support" });
122 list.appendAssumeCapacity(os_support_dir);
123
124 const config_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "config" });
125 list.appendAssumeCapacity(config_dir);
126 }
127
128 var sysroot: ?[]const u8 = null;
129
130 if (target.isDarwin()) d: {
131 const down1 = std.fs.path.dirname(lci.sys_include_dir.?) orelse break :d;
132 const down2 = std.fs.path.dirname(down1) orelse break :d;
133 try framework_list.append(try std.fs.path.join(arena, &.{ down2, "System", "Library", "Frameworks" }));
134 sysroot = down2;
135 }
136
137 return .{
138 .libc_include_dir_list = list.items,
139 .libc_installation = lci,
140 .libc_framework_dir_list = framework_list.items,
141 .sysroot = sysroot,
142 .darwin_sdk_layout = if (sysroot == null) null else .sdk,
143 };
144}
145
146pub fn detectFromBuilding(
147 arena: Allocator,
148 zig_lib_dir: []const u8,
149 target: std.Target,
150) !LibCDirs {
151 const s = std.fs.path.sep_str;
152
153 if (target.isDarwin()) {
154 const list = try arena.alloc([]const u8, 1);
155 list[0] = try std.fmt.allocPrint(
156 arena,
157 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-macos-any",
158 .{zig_lib_dir},
159 );
160 return .{
161 .libc_include_dir_list = list,
162 .libc_installation = null,
163 .libc_framework_dir_list = &.{},
164 .sysroot = null,
165 .darwin_sdk_layout = .vendored,
166 };
167 }
168
169 const generic_name = libCGenericName(target);
170 // Some architectures are handled by the same set of headers.
171 const arch_name = if (target.abi.isMusl())
172 std.zig.target.muslArchNameHeaders(target.cpu.arch)
173 else if (target.cpu.arch.isThumb())
174 // ARM headers are valid for Thumb too.
175 switch (target.cpu.arch) {
176 .thumb => "arm",
177 .thumbeb => "armeb",
178 else => unreachable,
179 }
180 else
181 @tagName(target.cpu.arch);
182 const os_name = @tagName(target.os.tag);
183 // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name.
184 const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi);
185 const arch_include_dir = try std.fmt.allocPrint(
186 arena,
187 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}",
188 .{ zig_lib_dir, arch_name, os_name, abi_name },
189 );
190 const generic_include_dir = try std.fmt.allocPrint(
191 arena,
192 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}",
193 .{ zig_lib_dir, generic_name },
194 );
195 const generic_arch_name = target.osArchName();
196 const arch_os_include_dir = try std.fmt.allocPrint(
197 arena,
198 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any",
199 .{ zig_lib_dir, generic_arch_name, os_name },
200 );
201 const generic_os_include_dir = try std.fmt.allocPrint(
202 arena,
203 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any",
204 .{ zig_lib_dir, os_name },
205 );
206
207 const list = try arena.alloc([]const u8, 4);
208 list[0] = arch_include_dir;
209 list[1] = generic_include_dir;
210 list[2] = arch_os_include_dir;
211 list[3] = generic_os_include_dir;
212
213 return .{
214 .libc_include_dir_list = list,
215 .libc_installation = null,
216 .libc_framework_dir_list = &.{},
217 .sysroot = null,
218 .darwin_sdk_layout = .vendored,
219 };
220}
221
222fn libCGenericName(target: std.Target) [:0]const u8 {
223 switch (target.os.tag) {
224 .windows => return "mingw",
225 .macos, .ios, .tvos, .watchos => return "darwin",
226 else => {},
227 }
228 switch (target.abi) {
229 .gnu,
230 .gnuabin32,
231 .gnuabi64,
232 .gnueabi,
233 .gnueabihf,
234 .gnuf32,
235 .gnuf64,
236 .gnusf,
237 .gnux32,
238 .gnuilp32,
239 => return "glibc",
240 .musl,
241 .musleabi,
242 .musleabihf,
243 .muslx32,
244 .none,
245 => return "musl",
246 .code16,
247 .eabi,
248 .eabihf,
249 .android,
250 .msvc,
251 .itanium,
252 .cygnus,
253 .coreclr,
254 .simulator,
255 .macabi,
256 => unreachable,
257
258 .pixel,
259 .vertex,
260 .geometry,
261 .hull,
262 .domain,
263 .compute,
264 .library,
265 .raygeneration,
266 .intersection,
267 .anyhit,
268 .closesthit,
269 .miss,
270 .callable,
271 .mesh,
272 .amplification,
273 => unreachable,
274 }
275}
276
277const LibCDirs = @This();
278const builtin = @import("builtin");
279const std = @import("../std.zig");
280const LibCInstallation = std.zig.LibCInstallation;
281const Allocator = std.mem.Allocator;
lib/std/zig/LibCInstallation.zig created+709
......@@ -0,0 +1,709 @@
1//! See the render function implementation for documentation of the fields.
2
3include_dir: ?[]const u8 = null,
4sys_include_dir: ?[]const u8 = null,
5crt_dir: ?[]const u8 = null,
6msvc_lib_dir: ?[]const u8 = null,
7kernel32_lib_dir: ?[]const u8 = null,
8gcc_dir: ?[]const u8 = null,
9
10pub const FindError = error{
11 OutOfMemory,
12 FileSystem,
13 UnableToSpawnCCompiler,
14 CCompilerExitCode,
15 CCompilerCrashed,
16 CCompilerCannotFindHeaders,
17 LibCRuntimeNotFound,
18 LibCStdLibHeaderNotFound,
19 LibCKernel32LibNotFound,
20 UnsupportedArchitecture,
21 WindowsSdkNotFound,
22 DarwinSdkNotFound,
23 ZigIsTheCCompiler,
24};
25
26pub fn parse(
27 allocator: Allocator,
28 libc_file: []const u8,
29 target: std.Target,
30) !LibCInstallation {
31 var self: LibCInstallation = .{};
32
33 const fields = std.meta.fields(LibCInstallation);
34 const FoundKey = struct {
35 found: bool,
36 allocated: ?[:0]u8,
37 };
38 var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** fields.len;
39 errdefer {
40 self = .{};
41 for (found_keys) |found_key| {
42 if (found_key.allocated) |s| allocator.free(s);
43 }
44 }
45
46 const contents = try std.fs.cwd().readFileAlloc(allocator, libc_file, std.math.maxInt(usize));
47 defer allocator.free(contents);
48
49 var it = std.mem.tokenizeScalar(u8, contents, '\n');
50 while (it.next()) |line| {
51 if (line.len == 0 or line[0] == '#') continue;
52 var line_it = std.mem.splitScalar(u8, line, '=');
53 const name = line_it.first();
54 const value = line_it.rest();
55 inline for (fields, 0..) |field, i| {
56 if (std.mem.eql(u8, name, field.name)) {
57 found_keys[i].found = true;
58 if (value.len == 0) {
59 @field(self, field.name) = null;
60 } else {
61 found_keys[i].allocated = try allocator.dupeZ(u8, value);
62 @field(self, field.name) = found_keys[i].allocated;
63 }
64 break;
65 }
66 }
67 }
68 inline for (fields, 0..) |field, i| {
69 if (!found_keys[i].found) {
70 log.err("missing field: {s}\n", .{field.name});
71 return error.ParseError;
72 }
73 }
74 if (self.include_dir == null) {
75 log.err("include_dir may not be empty\n", .{});
76 return error.ParseError;
77 }
78 if (self.sys_include_dir == null) {
79 log.err("sys_include_dir may not be empty\n", .{});
80 return error.ParseError;
81 }
82
83 const os_tag = target.os.tag;
84 if (self.crt_dir == null and !target.isDarwin()) {
85 log.err("crt_dir may not be empty for {s}\n", .{@tagName(os_tag)});
86 return error.ParseError;
87 }
88
89 if (self.msvc_lib_dir == null and os_tag == .windows and target.abi == .msvc) {
90 log.err("msvc_lib_dir may not be empty for {s}-{s}\n", .{
91 @tagName(os_tag),
92 @tagName(target.abi),
93 });
94 return error.ParseError;
95 }
96 if (self.kernel32_lib_dir == null and os_tag == .windows and target.abi == .msvc) {
97 log.err("kernel32_lib_dir may not be empty for {s}-{s}\n", .{
98 @tagName(os_tag),
99 @tagName(target.abi),
100 });
101 return error.ParseError;
102 }
103
104 if (self.gcc_dir == null and os_tag == .haiku) {
105 log.err("gcc_dir may not be empty for {s}\n", .{@tagName(os_tag)});
106 return error.ParseError;
107 }
108
109 return self;
110}
111
112pub fn render(self: LibCInstallation, out: anytype) !void {
113 @setEvalBranchQuota(4000);
114 const include_dir = self.include_dir orelse "";
115 const sys_include_dir = self.sys_include_dir orelse "";
116 const crt_dir = self.crt_dir orelse "";
117 const msvc_lib_dir = self.msvc_lib_dir orelse "";
118 const kernel32_lib_dir = self.kernel32_lib_dir orelse "";
119 const gcc_dir = self.gcc_dir orelse "";
120
121 try out.print(
122 \\# The directory that contains `stdlib.h`.
123 \\# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null`
124 \\include_dir={s}
125 \\
126 \\# The system-specific include directory. May be the same as `include_dir`.
127 \\# On Windows it's the directory that includes `vcruntime.h`.
128 \\# On POSIX it's the directory that includes `sys/errno.h`.
129 \\sys_include_dir={s}
130 \\
131 \\# The directory that contains `crt1.o` or `crt2.o`.
132 \\# On POSIX, can be found with `cc -print-file-name=crt1.o`.
133 \\# Not needed when targeting MacOS.
134 \\crt_dir={s}
135 \\
136 \\# The directory that contains `vcruntime.lib`.
137 \\# Only needed when targeting MSVC on Windows.
138 \\msvc_lib_dir={s}
139 \\
140 \\# The directory that contains `kernel32.lib`.
141 \\# Only needed when targeting MSVC on Windows.
142 \\kernel32_lib_dir={s}
143 \\
144 \\# The directory that contains `crtbeginS.o` and `crtendS.o`
145 \\# Only needed when targeting Haiku.
146 \\gcc_dir={s}
147 \\
148 , .{
149 include_dir,
150 sys_include_dir,
151 crt_dir,
152 msvc_lib_dir,
153 kernel32_lib_dir,
154 gcc_dir,
155 });
156}
157
158pub const FindNativeOptions = struct {
159 allocator: Allocator,
160 target: std.Target,
161
162 /// If enabled, will print human-friendly errors to stderr.
163 verbose: bool = false,
164};
165
166/// Finds the default, native libc.
167pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation {
168 var self: LibCInstallation = .{};
169
170 if (is_darwin) {
171 if (!std.zig.system.darwin.isSdkInstalled(args.allocator))
172 return error.DarwinSdkNotFound;
173 const sdk = std.zig.system.darwin.getSdk(args.allocator, args.target) orelse
174 return error.DarwinSdkNotFound;
175 defer args.allocator.free(sdk);
176
177 self.include_dir = try fs.path.join(args.allocator, &.{
178 sdk, "usr/include",
179 });
180 self.sys_include_dir = try fs.path.join(args.allocator, &.{
181 sdk, "usr/include",
182 });
183 return self;
184 } else if (is_windows) {
185 var sdk = std.zig.WindowsSdk.find(args.allocator) catch |err| switch (err) {
186 error.NotFound => return error.WindowsSdkNotFound,
187 error.PathTooLong => return error.WindowsSdkNotFound,
188 error.OutOfMemory => return error.OutOfMemory,
189 };
190 defer sdk.free(args.allocator);
191
192 try self.findNativeMsvcIncludeDir(args, &sdk);
193 try self.findNativeMsvcLibDir(args, &sdk);
194 try self.findNativeKernel32LibDir(args, &sdk);
195 try self.findNativeIncludeDirWindows(args, &sdk);
196 try self.findNativeCrtDirWindows(args, &sdk);
197 } else if (is_haiku) {
198 try self.findNativeIncludeDirPosix(args);
199 try self.findNativeCrtBeginDirHaiku(args);
200 self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib");
201 } else if (builtin.target.os.tag.isSolarish()) {
202 // There is only one libc, and its headers/libraries are always in the same spot.
203 self.include_dir = try args.allocator.dupeZ(u8, "/usr/include");
204 self.sys_include_dir = try args.allocator.dupeZ(u8, "/usr/include");
205 self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib/64");
206 } else if (std.process.can_spawn) {
207 try self.findNativeIncludeDirPosix(args);
208 switch (builtin.target.os.tag) {
209 .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib"),
210 .linux => try self.findNativeCrtDirPosix(args),
211 else => {},
212 }
213 } else {
214 return error.LibCRuntimeNotFound;
215 }
216 return self;
217}
218
219/// Must be the same allocator passed to `parse` or `findNative`.
220pub fn deinit(self: *LibCInstallation, allocator: Allocator) void {
221 const fields = std.meta.fields(LibCInstallation);
222 inline for (fields) |field| {
223 if (@field(self, field.name)) |payload| {
224 allocator.free(payload);
225 }
226 }
227 self.* = undefined;
228}
229
230fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
231 const allocator = args.allocator;
232
233 // Detect infinite loops.
234 var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
235 error.Unexpected => unreachable, // WASI-only
236 else => |e| return e,
237 };
238 defer env_map.deinit();
239 const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: {
240 if (std.mem.eql(u8, phase, "1")) {
241 try env_map.put(inf_loop_env_key, "2");
242 break :blk true;
243 } else {
244 return error.ZigIsTheCCompiler;
245 }
246 } else blk: {
247 try env_map.put(inf_loop_env_key, "1");
248 break :blk false;
249 };
250
251 const dev_null = if (is_windows) "nul" else "/dev/null";
252
253 var argv = std.ArrayList([]const u8).init(allocator);
254 defer argv.deinit();
255
256 try appendCcExe(&argv, skip_cc_env_var);
257 try argv.appendSlice(&.{
258 "-E",
259 "-Wp,-v",
260 "-xc",
261 dev_null,
262 });
263
264 const run_res = std.ChildProcess.run(.{
265 .allocator = allocator,
266 .argv = argv.items,
267 .max_output_bytes = 1024 * 1024,
268 .env_map = &env_map,
269 // Some C compilers, such as Clang, are known to rely on argv[0] to find the path
270 // to their own executable, without even bothering to resolve PATH. This results in the message:
271 // error: unable to execute command: Executable "" doesn't exist!
272 // So we use the expandArg0 variant of ChildProcess to give them a helping hand.
273 .expand_arg0 = .expand,
274 }) catch |err| switch (err) {
275 error.OutOfMemory => return error.OutOfMemory,
276 else => {
277 printVerboseInvocation(argv.items, null, args.verbose, null);
278 return error.UnableToSpawnCCompiler;
279 },
280 };
281 defer {
282 allocator.free(run_res.stdout);
283 allocator.free(run_res.stderr);
284 }
285 switch (run_res.term) {
286 .Exited => |code| if (code != 0) {
287 printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr);
288 return error.CCompilerExitCode;
289 },
290 else => {
291 printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr);
292 return error.CCompilerCrashed;
293 },
294 }
295
296 var it = std.mem.tokenizeAny(u8, run_res.stderr, "\n\r");
297 var search_paths = std.ArrayList([]const u8).init(allocator);
298 defer search_paths.deinit();
299 while (it.next()) |line| {
300 if (line.len != 0 and line[0] == ' ') {
301 try search_paths.append(line);
302 }
303 }
304 if (search_paths.items.len == 0) {
305 return error.CCompilerCannotFindHeaders;
306 }
307
308 const include_dir_example_file = if (is_haiku) "posix/stdlib.h" else "stdlib.h";
309 const sys_include_dir_example_file = if (is_windows)
310 "sys\\types.h"
311 else if (is_haiku)
312 "errno.h"
313 else
314 "sys/errno.h";
315
316 var path_i: usize = 0;
317 while (path_i < search_paths.items.len) : (path_i += 1) {
318 // search in reverse order
319 const search_path_untrimmed = search_paths.items[search_paths.items.len - path_i - 1];
320 const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
321 var search_dir = fs.cwd().openDir(search_path, .{}) catch |err| switch (err) {
322 error.FileNotFound,
323 error.NotDir,
324 error.NoDevice,
325 => continue,
326
327 else => return error.FileSystem,
328 };
329 defer search_dir.close();
330
331 if (self.include_dir == null) {
332 if (search_dir.accessZ(include_dir_example_file, .{})) |_| {
333 self.include_dir = try allocator.dupeZ(u8, search_path);
334 } else |err| switch (err) {
335 error.FileNotFound => {},
336 else => return error.FileSystem,
337 }
338 }
339
340 if (self.sys_include_dir == null) {
341 if (search_dir.accessZ(sys_include_dir_example_file, .{})) |_| {
342 self.sys_include_dir = try allocator.dupeZ(u8, search_path);
343 } else |err| switch (err) {
344 error.FileNotFound => {},
345 else => return error.FileSystem,
346 }
347 }
348
349 if (self.include_dir != null and self.sys_include_dir != null) {
350 // Success.
351 return;
352 }
353 }
354
355 return error.LibCStdLibHeaderNotFound;
356}
357
358fn findNativeIncludeDirWindows(
359 self: *LibCInstallation,
360 args: FindNativeOptions,
361 sdk: *std.zig.WindowsSdk,
362) FindError!void {
363 const allocator = args.allocator;
364
365 var search_buf: [2]Search = undefined;
366 const searches = fillSearch(&search_buf, sdk);
367
368 var result_buf = std.ArrayList(u8).init(allocator);
369 defer result_buf.deinit();
370
371 for (searches) |search| {
372 result_buf.shrinkAndFree(0);
373 try result_buf.writer().print("{s}\\Include\\{s}\\ucrt", .{ search.path, search.version });
374
375 var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) {
376 error.FileNotFound,
377 error.NotDir,
378 error.NoDevice,
379 => continue,
380
381 else => return error.FileSystem,
382 };
383 defer dir.close();
384
385 dir.accessZ("stdlib.h", .{}) catch |err| switch (err) {
386 error.FileNotFound => continue,
387 else => return error.FileSystem,
388 };
389
390 self.include_dir = try result_buf.toOwnedSlice();
391 return;
392 }
393
394 return error.LibCStdLibHeaderNotFound;
395}
396
397fn findNativeCrtDirWindows(
398 self: *LibCInstallation,
399 args: FindNativeOptions,
400 sdk: *std.zig.WindowsSdk,
401) FindError!void {
402 const allocator = args.allocator;
403
404 var search_buf: [2]Search = undefined;
405 const searches = fillSearch(&search_buf, sdk);
406
407 var result_buf = std.ArrayList(u8).init(allocator);
408 defer result_buf.deinit();
409
410 const arch_sub_dir = switch (builtin.target.cpu.arch) {
411 .x86 => "x86",
412 .x86_64 => "x64",
413 .arm, .armeb => "arm",
414 .aarch64 => "arm64",
415 else => return error.UnsupportedArchitecture,
416 };
417
418 for (searches) |search| {
419 result_buf.shrinkAndFree(0);
420 try result_buf.writer().print("{s}\\Lib\\{s}\\ucrt\\{s}", .{ search.path, search.version, arch_sub_dir });
421
422 var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) {
423 error.FileNotFound,
424 error.NotDir,
425 error.NoDevice,
426 => continue,
427
428 else => return error.FileSystem,
429 };
430 defer dir.close();
431
432 dir.accessZ("ucrt.lib", .{}) catch |err| switch (err) {
433 error.FileNotFound => continue,
434 else => return error.FileSystem,
435 };
436
437 self.crt_dir = try result_buf.toOwnedSlice();
438 return;
439 }
440 return error.LibCRuntimeNotFound;
441}
442
443fn findNativeCrtDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
444 self.crt_dir = try ccPrintFileName(.{
445 .allocator = args.allocator,
446 .search_basename = "crt1.o",
447 .want_dirname = .only_dir,
448 .verbose = args.verbose,
449 });
450}
451
452fn findNativeCrtBeginDirHaiku(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
453 self.gcc_dir = try ccPrintFileName(.{
454 .allocator = args.allocator,
455 .search_basename = "crtbeginS.o",
456 .want_dirname = .only_dir,
457 .verbose = args.verbose,
458 });
459}
460
461fn findNativeKernel32LibDir(
462 self: *LibCInstallation,
463 args: FindNativeOptions,
464 sdk: *std.zig.WindowsSdk,
465) FindError!void {
466 const allocator = args.allocator;
467
468 var search_buf: [2]Search = undefined;
469 const searches = fillSearch(&search_buf, sdk);
470
471 var result_buf = std.ArrayList(u8).init(allocator);
472 defer result_buf.deinit();
473
474 const arch_sub_dir = switch (builtin.target.cpu.arch) {
475 .x86 => "x86",
476 .x86_64 => "x64",
477 .arm, .armeb => "arm",
478 .aarch64 => "arm64",
479 else => return error.UnsupportedArchitecture,
480 };
481
482 for (searches) |search| {
483 result_buf.shrinkAndFree(0);
484 const stream = result_buf.writer();
485 try stream.print("{s}\\Lib\\{s}\\um\\{s}", .{ search.path, search.version, arch_sub_dir });
486
487 var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) {
488 error.FileNotFound,
489 error.NotDir,
490 error.NoDevice,
491 => continue,
492
493 else => return error.FileSystem,
494 };
495 defer dir.close();
496
497 dir.accessZ("kernel32.lib", .{}) catch |err| switch (err) {
498 error.FileNotFound => continue,
499 else => return error.FileSystem,
500 };
501
502 self.kernel32_lib_dir = try result_buf.toOwnedSlice();
503 return;
504 }
505 return error.LibCKernel32LibNotFound;
506}
507
508fn findNativeMsvcIncludeDir(
509 self: *LibCInstallation,
510 args: FindNativeOptions,
511 sdk: *std.zig.WindowsSdk,
512) FindError!void {
513 const allocator = args.allocator;
514
515 const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCStdLibHeaderNotFound;
516 const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound;
517 const up2 = fs.path.dirname(up1) orelse return error.LibCStdLibHeaderNotFound;
518
519 const dir_path = try fs.path.join(allocator, &[_][]const u8{ up2, "include" });
520 errdefer allocator.free(dir_path);
521
522 var dir = fs.cwd().openDir(dir_path, .{}) catch |err| switch (err) {
523 error.FileNotFound,
524 error.NotDir,
525 error.NoDevice,
526 => return error.LibCStdLibHeaderNotFound,
527
528 else => return error.FileSystem,
529 };
530 defer dir.close();
531
532 dir.accessZ("vcruntime.h", .{}) catch |err| switch (err) {
533 error.FileNotFound => return error.LibCStdLibHeaderNotFound,
534 else => return error.FileSystem,
535 };
536
537 self.sys_include_dir = dir_path;
538}
539
540fn findNativeMsvcLibDir(
541 self: *LibCInstallation,
542 args: FindNativeOptions,
543 sdk: *std.zig.WindowsSdk,
544) FindError!void {
545 const allocator = args.allocator;
546 const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCRuntimeNotFound;
547 self.msvc_lib_dir = try allocator.dupe(u8, msvc_lib_dir);
548}
549
550pub const CCPrintFileNameOptions = struct {
551 allocator: Allocator,
552 search_basename: []const u8,
553 want_dirname: enum { full_path, only_dir },
554 verbose: bool = false,
555};
556
557/// caller owns returned memory
558fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 {
559 const allocator = args.allocator;
560
561 // Detect infinite loops.
562 var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
563 error.Unexpected => unreachable, // WASI-only
564 else => |e| return e,
565 };
566 defer env_map.deinit();
567 const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: {
568 if (std.mem.eql(u8, phase, "1")) {
569 try env_map.put(inf_loop_env_key, "2");
570 break :blk true;
571 } else {
572 return error.ZigIsTheCCompiler;
573 }
574 } else blk: {
575 try env_map.put(inf_loop_env_key, "1");
576 break :blk false;
577 };
578
579 var argv = std.ArrayList([]const u8).init(allocator);
580 defer argv.deinit();
581
582 const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={s}", .{args.search_basename});
583 defer allocator.free(arg1);
584
585 try appendCcExe(&argv, skip_cc_env_var);
586 try argv.append(arg1);
587
588 const run_res = std.ChildProcess.run(.{
589 .allocator = allocator,
590 .argv = argv.items,
591 .max_output_bytes = 1024 * 1024,
592 .env_map = &env_map,
593 // Some C compilers, such as Clang, are known to rely on argv[0] to find the path
594 // to their own executable, without even bothering to resolve PATH. This results in the message:
595 // error: unable to execute command: Executable "" doesn't exist!
596 // So we use the expandArg0 variant of ChildProcess to give them a helping hand.
597 .expand_arg0 = .expand,
598 }) catch |err| switch (err) {
599 error.OutOfMemory => return error.OutOfMemory,
600 else => return error.UnableToSpawnCCompiler,
601 };
602 defer {
603 allocator.free(run_res.stdout);
604 allocator.free(run_res.stderr);
605 }
606 switch (run_res.term) {
607 .Exited => |code| if (code != 0) {
608 printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr);
609 return error.CCompilerExitCode;
610 },
611 else => {
612 printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr);
613 return error.CCompilerCrashed;
614 },
615 }
616
617 var it = std.mem.tokenizeAny(u8, run_res.stdout, "\n\r");
618 const line = it.next() orelse return error.LibCRuntimeNotFound;
619 // When this command fails, it returns exit code 0 and duplicates the input file name.
620 // So we detect failure by checking if the output matches exactly the input.
621 if (std.mem.eql(u8, line, args.search_basename)) return error.LibCRuntimeNotFound;
622 switch (args.want_dirname) {
623 .full_path => return allocator.dupeZ(u8, line),
624 .only_dir => {
625 const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
626 return allocator.dupeZ(u8, dirname);
627 },
628 }
629}
630
631fn printVerboseInvocation(
632 argv: []const []const u8,
633 search_basename: ?[]const u8,
634 verbose: bool,
635 stderr: ?[]const u8,
636) void {
637 if (!verbose) return;
638
639 if (search_basename) |s| {
640 std.debug.print("Zig attempted to find the file '{s}' by executing this command:\n", .{s});
641 } else {
642 std.debug.print("Zig attempted to find the path to native system libc headers by executing this command:\n", .{});
643 }
644 for (argv, 0..) |arg, i| {
645 if (i != 0) std.debug.print(" ", .{});
646 std.debug.print("{s}", .{arg});
647 }
648 std.debug.print("\n", .{});
649 if (stderr) |s| {
650 std.debug.print("Output:\n==========\n{s}\n==========\n", .{s});
651 }
652}
653
654const Search = struct {
655 path: []const u8,
656 version: []const u8,
657};
658
659fn fillSearch(search_buf: *[2]Search, sdk: *std.zig.WindowsSdk) []Search {
660 var search_end: usize = 0;
661 if (sdk.windows10sdk) |windows10sdk| {
662 search_buf[search_end] = .{
663 .path = windows10sdk.path,
664 .version = windows10sdk.version,
665 };
666 search_end += 1;
667 }
668 if (sdk.windows81sdk) |windows81sdk| {
669 search_buf[search_end] = .{
670 .path = windows81sdk.path,
671 .version = windows81sdk.version,
672 };
673 search_end += 1;
674 }
675 return search_buf[0..search_end];
676}
677
678const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS";
679
680fn appendCcExe(args: *std.ArrayList([]const u8), skip_cc_env_var: bool) !void {
681 const default_cc_exe = if (is_windows) "cc.exe" else "cc";
682 try args.ensureUnusedCapacity(1);
683 if (skip_cc_env_var) {
684 args.appendAssumeCapacity(default_cc_exe);
685 return;
686 }
687 const cc_env_var = std.zig.EnvVar.CC.getPosix() orelse {
688 args.appendAssumeCapacity(default_cc_exe);
689 return;
690 };
691 // Respect space-separated flags to the C compiler.
692 var it = std.mem.tokenizeScalar(u8, cc_env_var, ' ');
693 while (it.next()) |arg| {
694 try args.append(arg);
695 }
696}
697
698const LibCInstallation = @This();
699const std = @import("std");
700const builtin = @import("builtin");
701const Target = std.Target;
702const fs = std.fs;
703const Allocator = std.mem.Allocator;
704
705const is_darwin = builtin.target.isDarwin();
706const is_windows = builtin.target.os.tag == .windows;
707const is_haiku = builtin.target.os.tag == .haiku;
708
709const log = std.log.scoped(.libc_installation);
lib/std/zig/WindowsSdk.zig created+964
......@@ -0,0 +1,964 @@
1windows10sdk: ?Windows10Sdk,
2windows81sdk: ?Windows81Sdk,
3msvc_lib_dir: ?[]const u8,
4
5const WindowsSdk = @This();
6const std = @import("std");
7const builtin = @import("builtin");
8
9const windows = std.os.windows;
10const RRF = windows.advapi32.RRF;
11
12const WINDOWS_KIT_REG_KEY = "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots";
13
14// https://learn.microsoft.com/en-us/windows/win32/msi/productversion
15const version_major_minor_max_length = "255.255".len;
16// note(bratishkaerik): i think ProductVersion in registry (created by Visual Studio installer) also follows this rule
17const product_version_max_length = version_major_minor_max_length + ".65535".len;
18
19/// Find path and version of Windows 10 SDK and Windows 8.1 SDK, and find path to MSVC's `lib/` directory.
20/// Caller owns the result's fields.
21/// After finishing work, call `free(allocator)`.
22pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, NotFound, PathTooLong }!WindowsSdk {
23 if (builtin.os.tag != .windows) return error.NotFound;
24
25 //note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed
26 const roots_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, WINDOWS_KIT_REG_KEY) catch |err| switch (err) {
27 error.KeyNotFound => return error.NotFound,
28 };
29 defer roots_key.closeKey();
30
31 const windows10sdk: ?Windows10Sdk = blk: {
32 const windows10sdk = Windows10Sdk.find(allocator) catch |err| switch (err) {
33 error.Windows10SdkNotFound,
34 error.PathTooLong,
35 error.VersionTooLong,
36 => break :blk null,
37 error.OutOfMemory => return error.OutOfMemory,
38 };
39 const is_valid_version = windows10sdk.isValidVersion();
40 if (!is_valid_version) break :blk null;
41 break :blk windows10sdk;
42 };
43 errdefer if (windows10sdk) |*w| w.free(allocator);
44
45 const windows81sdk: ?Windows81Sdk = blk: {
46 const windows81sdk = Windows81Sdk.find(allocator, &roots_key) catch |err| switch (err) {
47 error.Windows81SdkNotFound => break :blk null,
48 error.PathTooLong => break :blk null,
49 error.VersionTooLong => break :blk null,
50 error.OutOfMemory => return error.OutOfMemory,
51 };
52 // no check
53 break :blk windows81sdk;
54 };
55 errdefer if (windows81sdk) |*w| w.free(allocator);
56
57 const msvc_lib_dir: ?[]const u8 = MsvcLibDir.find(allocator) catch |err| switch (err) {
58 error.MsvcLibDirNotFound => null,
59 error.OutOfMemory => return error.OutOfMemory,
60 };
61 errdefer allocator.free(msvc_lib_dir);
62
63 return WindowsSdk{
64 .windows10sdk = windows10sdk,
65 .windows81sdk = windows81sdk,
66 .msvc_lib_dir = msvc_lib_dir,
67 };
68}
69
70pub fn free(self: *const WindowsSdk, allocator: std.mem.Allocator) void {
71 if (self.windows10sdk) |*w10sdk| {
72 w10sdk.free(allocator);
73 }
74 if (self.windows81sdk) |*w81sdk| {
75 w81sdk.free(allocator);
76 }
77 if (self.msvc_lib_dir) |msvc_lib_dir| {
78 allocator.free(msvc_lib_dir);
79 }
80}
81
82/// Iterates via `iterator` and collects all folders with names starting with `optional_prefix`
83/// and similar to SemVer. Returns slice of folder names sorted in descending order.
84/// Caller owns result.
85fn iterateAndFilterBySemVer(
86 iterator: *std.fs.Dir.Iterator,
87 allocator: std.mem.Allocator,
88 comptime optional_prefix: ?[]const u8,
89) error{ OutOfMemory, VersionNotFound }![][]const u8 {
90 var dirs_filtered_list = std.ArrayList([]const u8).init(allocator);
91 errdefer {
92 for (dirs_filtered_list.items) |filtered_dir| allocator.free(filtered_dir);
93 dirs_filtered_list.deinit();
94 }
95
96 var normalized_name_buf: [std.fs.MAX_NAME_BYTES + ".0+build.0".len]u8 = undefined;
97 var normalized_name_fbs = std.io.fixedBufferStream(&normalized_name_buf);
98 const normalized_name_w = normalized_name_fbs.writer();
99 iterate_folder: while (true) : (normalized_name_fbs.reset()) {
100 const maybe_entry = iterator.next() catch continue :iterate_folder;
101 const entry = maybe_entry orelse break :iterate_folder;
102
103 if (entry.kind != .directory)
104 continue :iterate_folder;
105
106 // invalidated on next iteration
107 const subfolder_name = blk: {
108 if (comptime optional_prefix) |prefix| {
109 if (!std.mem.startsWith(u8, entry.name, prefix)) continue :iterate_folder;
110 break :blk entry.name[prefix.len..];
111 } else break :blk entry.name;
112 };
113
114 { // check if subfolder name looks similar to SemVer
115 switch (std.mem.count(u8, subfolder_name, ".")) {
116 0 => normalized_name_w.print("{s}.0.0+build.0", .{subfolder_name}) catch unreachable, // 17 => 17.0.0+build.0
117 1 => if (std.mem.indexOfScalar(u8, subfolder_name, '_')) |underscore_pos| blk: { // 17.0_9e9cbb98 => 17.0.1+build.9e9cbb98
118 var subfolder_name_tmp_copy_buf: [std.fs.MAX_NAME_BYTES]u8 = undefined;
119 const subfolder_name_tmp_copy = subfolder_name_tmp_copy_buf[0..subfolder_name.len];
120 @memcpy(subfolder_name_tmp_copy, subfolder_name);
121
122 subfolder_name_tmp_copy[underscore_pos] = '.'; // 17.0_9e9cbb98 => 17.0.9e9cbb98
123 var subfolder_name_parts = std.mem.splitScalar(u8, subfolder_name_tmp_copy, '.'); // [ 17, 0, 9e9cbb98 ]
124
125 const first = subfolder_name_parts.first(); // 17
126 const second = subfolder_name_parts.next().?; // 0
127 const third = subfolder_name_parts.rest(); // 9e9cbb98
128
129 break :blk normalized_name_w.print("{s}.{s}.1+build.{s}", .{ first, second, third }) catch unreachable; // [ 17, 0, 9e9cbb98 ] => 17.0.1+build.9e9cbb98
130 } else normalized_name_w.print("{s}.0+build.0", .{subfolder_name}) catch unreachable, // 17.0 => 17.0.0+build.0
131 else => normalized_name_w.print("{s}+build.0", .{subfolder_name}) catch unreachable, // 17.0.0 => 17.0.0+build.0
132 }
133 const subfolder_name_normalized: []const u8 = normalized_name_fbs.getWritten();
134 const sem_ver = std.SemanticVersion.parse(subfolder_name_normalized);
135 _ = sem_ver catch continue :iterate_folder;
136 }
137 // entry.name passed check
138
139 const subfolder_name_allocated = try allocator.dupe(u8, subfolder_name);
140 errdefer allocator.free(subfolder_name_allocated);
141 try dirs_filtered_list.append(subfolder_name_allocated);
142 }
143
144 const dirs_filtered_slice = try dirs_filtered_list.toOwnedSlice();
145 // Keep in mind that order of these names is not guaranteed by Windows,
146 // so we cannot just reverse or "while (popOrNull())" this ArrayList.
147 std.mem.sortUnstable([]const u8, dirs_filtered_slice, {}, struct {
148 fn desc(_: void, lhs: []const u8, rhs: []const u8) bool {
149 return std.mem.order(u8, lhs, rhs) == .gt;
150 }
151 }.desc);
152 return dirs_filtered_slice;
153}
154
155const RegistryWtf8 = struct {
156 key: windows.HKEY,
157
158 /// Assert that `key` is valid WTF-8 string
159 pub fn openKey(hkey: windows.HKEY, key: []const u8) error{KeyNotFound}!RegistryWtf8 {
160 const key_wtf16le: [:0]const u16 = key_wtf16le: {
161 var key_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined;
162 const key_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(key_wtf16le_buf[0..], key) catch |err| switch (err) {
163 error.InvalidWtf8 => unreachable,
164 };
165 key_wtf16le_buf[key_wtf16le_len] = 0;
166 break :key_wtf16le key_wtf16le_buf[0..key_wtf16le_len :0];
167 };
168
169 const registry_wtf16le = try RegistryWtf16Le.openKey(hkey, key_wtf16le);
170 return RegistryWtf8{ .key = registry_wtf16le.key };
171 }
172
173 /// Closes key, after that usage is invalid
174 pub fn closeKey(self: *const RegistryWtf8) void {
175 const return_code_int: windows.HRESULT = windows.advapi32.RegCloseKey(self.key);
176 const return_code: windows.Win32Error = @enumFromInt(return_code_int);
177 switch (return_code) {
178 .SUCCESS => {},
179 else => {},
180 }
181 }
182
183 /// Get string from registry.
184 /// Caller owns result.
185 pub fn getString(self: *const RegistryWtf8, allocator: std.mem.Allocator, subkey: []const u8, value_name: []const u8) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]u8 {
186 const subkey_wtf16le: [:0]const u16 = subkey_wtf16le: {
187 var subkey_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined;
188 const subkey_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(subkey_wtf16le_buf[0..], subkey) catch unreachable;
189 subkey_wtf16le_buf[subkey_wtf16le_len] = 0;
190 break :subkey_wtf16le subkey_wtf16le_buf[0..subkey_wtf16le_len :0];
191 };
192
193 const value_name_wtf16le: [:0]const u16 = value_name_wtf16le: {
194 var value_name_wtf16le_buf: [RegistryWtf16Le.value_name_max_len]u16 = undefined;
195 const value_name_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(value_name_wtf16le_buf[0..], value_name) catch unreachable;
196 value_name_wtf16le_buf[value_name_wtf16le_len] = 0;
197 break :value_name_wtf16le value_name_wtf16le_buf[0..value_name_wtf16le_len :0];
198 };
199
200 const registry_wtf16le = RegistryWtf16Le{ .key = self.key };
201 const value_wtf16le = try registry_wtf16le.getString(allocator, subkey_wtf16le, value_name_wtf16le);
202 defer allocator.free(value_wtf16le);
203
204 const value_wtf8: []u8 = try std.unicode.wtf16LeToWtf8Alloc(allocator, value_wtf16le);
205 errdefer allocator.free(value_wtf8);
206
207 return value_wtf8;
208 }
209
210 /// Get DWORD (u32) from registry.
211 pub fn getDword(self: *const RegistryWtf8, subkey: []const u8, value_name: []const u8) error{ ValueNameNotFound, NotADword, DwordTooLong, DwordNotFound }!u32 {
212 const subkey_wtf16le: [:0]const u16 = subkey_wtf16le: {
213 var subkey_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined;
214 const subkey_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(subkey_wtf16le_buf[0..], subkey) catch unreachable;
215 subkey_wtf16le_buf[subkey_wtf16le_len] = 0;
216 break :subkey_wtf16le subkey_wtf16le_buf[0..subkey_wtf16le_len :0];
217 };
218
219 const value_name_wtf16le: [:0]const u16 = value_name_wtf16le: {
220 var value_name_wtf16le_buf: [RegistryWtf16Le.value_name_max_len]u16 = undefined;
221 const value_name_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(value_name_wtf16le_buf[0..], value_name) catch unreachable;
222 value_name_wtf16le_buf[value_name_wtf16le_len] = 0;
223 break :value_name_wtf16le value_name_wtf16le_buf[0..value_name_wtf16le_len :0];
224 };
225
226 const registry_wtf16le = RegistryWtf16Le{ .key = self.key };
227 return try registry_wtf16le.getDword(subkey_wtf16le, value_name_wtf16le);
228 }
229
230 /// Under private space with flags:
231 /// KEY_QUERY_VALUE and KEY_ENUMERATE_SUB_KEYS.
232 /// After finishing work, call `closeKey`.
233 pub fn loadFromPath(absolute_path: []const u8) error{KeyNotFound}!RegistryWtf8 {
234 const absolute_path_wtf16le: [:0]const u16 = absolute_path_wtf16le: {
235 var absolute_path_wtf16le_buf: [RegistryWtf16Le.value_name_max_len]u16 = undefined;
236 const absolute_path_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(absolute_path_wtf16le_buf[0..], absolute_path) catch unreachable;
237 absolute_path_wtf16le_buf[absolute_path_wtf16le_len] = 0;
238 break :absolute_path_wtf16le absolute_path_wtf16le_buf[0..absolute_path_wtf16le_len :0];
239 };
240
241 const registry_wtf16le = try RegistryWtf16Le.loadFromPath(absolute_path_wtf16le);
242 return RegistryWtf8{ .key = registry_wtf16le.key };
243 }
244};
245
246const RegistryWtf16Le = struct {
247 key: windows.HKEY,
248
249 /// Includes root key (f.e. HKEY_LOCAL_MACHINE).
250 /// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
251 pub const key_name_max_len = 255;
252 /// In Unicode characters.
253 /// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
254 pub const value_name_max_len = 16_383;
255
256 /// Under HKEY_LOCAL_MACHINE with flags:
257 /// KEY_QUERY_VALUE, KEY_WOW64_32KEY, and KEY_ENUMERATE_SUB_KEYS.
258 /// After finishing work, call `closeKey`.
259 fn openKey(hkey: windows.HKEY, key_wtf16le: [:0]const u16) error{KeyNotFound}!RegistryWtf16Le {
260 var key: windows.HKEY = undefined;
261 const return_code_int: windows.HRESULT = windows.advapi32.RegOpenKeyExW(
262 hkey,
263 key_wtf16le,
264 0,
265 windows.KEY_QUERY_VALUE | windows.KEY_WOW64_32KEY | windows.KEY_ENUMERATE_SUB_KEYS,
266 &key,
267 );
268 const return_code: windows.Win32Error = @enumFromInt(return_code_int);
269 switch (return_code) {
270 .SUCCESS => {},
271 .FILE_NOT_FOUND => return error.KeyNotFound,
272
273 else => return error.KeyNotFound,
274 }
275 return RegistryWtf16Le{ .key = key };
276 }
277
278 /// Closes key, after that usage is invalid
279 fn closeKey(self: *const RegistryWtf16Le) void {
280 const return_code_int: windows.HRESULT = windows.advapi32.RegCloseKey(self.key);
281 const return_code: windows.Win32Error = @enumFromInt(return_code_int);
282 switch (return_code) {
283 .SUCCESS => {},
284 else => {},
285 }
286 }
287
288 /// Get string ([:0]const u16) from registry.
289 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 {
290 var actual_type: windows.ULONG = undefined;
291
292 // Calculating length to allocate
293 var value_wtf16le_buf_size: u32 = 0; // in bytes, including any terminating NUL character or characters.
294 var return_code_int: windows.HRESULT = windows.advapi32.RegGetValueW(
295 self.key,
296 subkey_wtf16le,
297 value_name_wtf16le,
298 RRF.RT_REG_SZ,
299 &actual_type,
300 null,
301 &value_wtf16le_buf_size,
302 );
303
304 // Check returned code and type
305 var return_code: windows.Win32Error = @enumFromInt(return_code_int);
306 switch (return_code) {
307 .SUCCESS => std.debug.assert(value_wtf16le_buf_size != 0),
308 .MORE_DATA => unreachable, // We are only reading length
309 .FILE_NOT_FOUND => return error.ValueNameNotFound,
310 .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY
311 else => return error.StringNotFound,
312 }
313 switch (actual_type) {
314 windows.REG.SZ => {},
315 else => return error.NotAString,
316 }
317
318 const value_wtf16le_buf: []u16 = try allocator.alloc(u16, std.math.divCeil(u32, value_wtf16le_buf_size, 2) catch unreachable);
319 errdefer allocator.free(value_wtf16le_buf);
320
321 return_code_int = windows.advapi32.RegGetValueW(
322 self.key,
323 subkey_wtf16le,
324 value_name_wtf16le,
325 RRF.RT_REG_SZ,
326 &actual_type,
327 value_wtf16le_buf.ptr,
328 &value_wtf16le_buf_size,
329 );
330
331 // Check returned code and (just in case) type again.
332 return_code = @enumFromInt(return_code_int);
333 switch (return_code) {
334 .SUCCESS => {},
335 .MORE_DATA => unreachable, // Calculated first time length should be enough, even overestimated
336 .FILE_NOT_FOUND => return error.ValueNameNotFound,
337 .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY
338 else => return error.StringNotFound,
339 }
340 switch (actual_type) {
341 windows.REG.SZ => {},
342 else => return error.NotAString,
343 }
344
345 const value_wtf16le: []const u16 = value_wtf16le: {
346 // note(bratishkaerik): somehow returned value in `buf_len` is overestimated by Windows and contains extra space
347 // we will just search for zero termination and forget length
348 // Windows sure is strange
349 const value_wtf16le_overestimated: [*:0]const u16 = @ptrCast(value_wtf16le_buf.ptr);
350 break :value_wtf16le std.mem.span(value_wtf16le_overestimated);
351 };
352
353 _ = allocator.resize(value_wtf16le_buf, value_wtf16le.len);
354 return value_wtf16le;
355 }
356
357 /// Get DWORD (u32) from registry.
358 fn getDword(self: *const RegistryWtf16Le, subkey_wtf16le: [:0]const u16, value_name_wtf16le: [:0]const u16) error{ ValueNameNotFound, NotADword, DwordTooLong, DwordNotFound }!u32 {
359 var actual_type: windows.ULONG = undefined;
360 var reg_size: u32 = @sizeOf(u32);
361 var reg_value: u32 = 0;
362
363 const return_code_int: windows.HRESULT = windows.advapi32.RegGetValueW(
364 self.key,
365 subkey_wtf16le,
366 value_name_wtf16le,
367 RRF.RT_REG_DWORD,
368 &actual_type,
369 &reg_value,
370 &reg_size,
371 );
372 const return_code: windows.Win32Error = @enumFromInt(return_code_int);
373 switch (return_code) {
374 .SUCCESS => {},
375 .MORE_DATA => return error.DwordTooLong,
376 .FILE_NOT_FOUND => return error.ValueNameNotFound,
377 .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY
378 else => return error.DwordNotFound,
379 }
380
381 switch (actual_type) {
382 windows.REG.DWORD => {},
383 else => return error.NotADword,
384 }
385
386 return reg_value;
387 }
388
389 /// Under private space with flags:
390 /// KEY_QUERY_VALUE and KEY_ENUMERATE_SUB_KEYS.
391 /// After finishing work, call `closeKey`.
392 fn loadFromPath(absolute_path_as_wtf16le: [:0]const u16) error{KeyNotFound}!RegistryWtf16Le {
393 var key: windows.HKEY = undefined;
394
395 const return_code_int: windows.HRESULT = std.os.windows.advapi32.RegLoadAppKeyW(
396 absolute_path_as_wtf16le,
397 &key,
398 windows.KEY_QUERY_VALUE | windows.KEY_ENUMERATE_SUB_KEYS,
399 0,
400 0,
401 );
402 const return_code: windows.Win32Error = @enumFromInt(return_code_int);
403 switch (return_code) {
404 .SUCCESS => {},
405 else => return error.KeyNotFound,
406 }
407
408 return RegistryWtf16Le{ .key = key };
409 }
410};
411
412pub const Windows10Sdk = struct {
413 path: []const u8,
414 version: []const u8,
415
416 /// Find path and version of Windows 10 SDK.
417 /// Caller owns the result's fields.
418 /// After finishing work, call `free(allocator)`.
419 fn find(allocator: std.mem.Allocator) error{ OutOfMemory, Windows10SdkNotFound, PathTooLong, VersionTooLong }!Windows10Sdk {
420 const v10_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0") catch |err| switch (err) {
421 error.KeyNotFound => return error.Windows10SdkNotFound,
422 };
423 defer v10_key.closeKey();
424
425 const path: []const u8 = path10: {
426 const path_maybe_with_trailing_slash = v10_key.getString(allocator, "", "InstallationFolder") catch |err| switch (err) {
427 error.NotAString => return error.Windows10SdkNotFound,
428 error.ValueNameNotFound => return error.Windows10SdkNotFound,
429 error.StringNotFound => return error.Windows10SdkNotFound,
430
431 error.OutOfMemory => return error.OutOfMemory,
432 };
433
434 if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
435 allocator.free(path_maybe_with_trailing_slash);
436 return error.PathTooLong;
437 }
438
439 var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
440 errdefer path.deinit();
441
442 // String might contain trailing slash, so trim it here
443 if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
444
445 const path_without_trailing_slash = try path.toOwnedSlice();
446 break :path10 path_without_trailing_slash;
447 };
448 errdefer allocator.free(path);
449
450 const version: []const u8 = version10: {
451
452 // note(dimenus): Microsoft doesn't include the .0 in the ProductVersion key....
453 const version_without_0 = v10_key.getString(allocator, "", "ProductVersion") catch |err| switch (err) {
454 error.NotAString => return error.Windows10SdkNotFound,
455 error.ValueNameNotFound => return error.Windows10SdkNotFound,
456 error.StringNotFound => return error.Windows10SdkNotFound,
457
458 error.OutOfMemory => return error.OutOfMemory,
459 };
460 if (version_without_0.len + ".0".len > product_version_max_length) {
461 allocator.free(version_without_0);
462 return error.VersionTooLong;
463 }
464
465 var version = std.ArrayList(u8).fromOwnedSlice(allocator, version_without_0);
466 errdefer version.deinit();
467
468 try version.appendSlice(".0");
469
470 const version_with_0 = try version.toOwnedSlice();
471 break :version10 version_with_0;
472 };
473 errdefer allocator.free(version);
474
475 return Windows10Sdk{ .path = path, .version = version };
476 }
477
478 /// Check whether this version is enumerated in registry.
479 fn isValidVersion(windows10sdk: *const Windows10Sdk) bool {
480 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
481 const reg_query_as_wtf8 = std.fmt.bufPrint(buf[0..], "{s}\\{s}\\Installed Options", .{ WINDOWS_KIT_REG_KEY, windows10sdk.version }) catch |err| switch (err) {
482 error.NoSpaceLeft => return false,
483 };
484
485 const options_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, reg_query_as_wtf8) catch |err| switch (err) {
486 error.KeyNotFound => return false,
487 };
488 defer options_key.closeKey();
489
490 const option_name = comptime switch (builtin.target.cpu.arch) {
491 .arm, .armeb => "OptionId.DesktopCPParm",
492 .aarch64 => "OptionId.DesktopCPParm64",
493 .x86_64 => "OptionId.DesktopCPPx64",
494 .x86 => "OptionId.DesktopCPPx86",
495 else => |tag| @compileError("Windows 10 SDK cannot be detected on architecture " ++ tag),
496 };
497
498 const reg_value = options_key.getDword("", option_name) catch return false;
499 return (reg_value == 1);
500 }
501
502 fn free(self: *const Windows10Sdk, allocator: std.mem.Allocator) void {
503 allocator.free(self.path);
504 allocator.free(self.version);
505 }
506};
507
508pub const Windows81Sdk = struct {
509 path: []const u8,
510 version: []const u8,
511
512 /// Find path and version of Windows 8.1 SDK.
513 /// Caller owns the result's fields.
514 /// After finishing work, call `free(allocator)`.
515 fn find(allocator: std.mem.Allocator, roots_key: *const RegistryWtf8) error{ OutOfMemory, Windows81SdkNotFound, PathTooLong, VersionTooLong }!Windows81Sdk {
516 const path: []const u8 = path81: {
517 const path_maybe_with_trailing_slash = roots_key.getString(allocator, "", "KitsRoot81") catch |err| switch (err) {
518 error.NotAString => return error.Windows81SdkNotFound,
519 error.ValueNameNotFound => return error.Windows81SdkNotFound,
520 error.StringNotFound => return error.Windows81SdkNotFound,
521
522 error.OutOfMemory => return error.OutOfMemory,
523 };
524 if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
525 allocator.free(path_maybe_with_trailing_slash);
526 return error.PathTooLong;
527 }
528
529 var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
530 errdefer path.deinit();
531
532 // String might contain trailing slash, so trim it here
533 if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
534
535 const path_without_trailing_slash = try path.toOwnedSlice();
536 break :path81 path_without_trailing_slash;
537 };
538 errdefer allocator.free(path);
539
540 const version: []const u8 = version81: {
541 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
542 const sdk_lib_dir_path = std.fmt.bufPrint(buf[0..], "{s}\\Lib\\", .{path}) catch |err| switch (err) {
543 error.NoSpaceLeft => return error.PathTooLong,
544 };
545 if (!std.fs.path.isAbsolute(sdk_lib_dir_path)) return error.Windows81SdkNotFound;
546
547 // enumerate files in sdk path looking for latest version
548 var sdk_lib_dir = std.fs.openDirAbsolute(sdk_lib_dir_path, .{
549 .iterate = true,
550 }) catch |err| switch (err) {
551 error.NameTooLong => return error.PathTooLong,
552 else => return error.Windows81SdkNotFound,
553 };
554 defer sdk_lib_dir.close();
555
556 var iterator = sdk_lib_dir.iterate();
557 const versions = iterateAndFilterBySemVer(&iterator, allocator, "winv") catch |err| switch (err) {
558 error.OutOfMemory => return error.OutOfMemory,
559 error.VersionNotFound => return error.Windows81SdkNotFound,
560 };
561 defer {
562 for (versions) |version| allocator.free(version);
563 allocator.free(versions);
564 }
565 const latest_version = try allocator.dupe(u8, versions[0]);
566 break :version81 latest_version;
567 };
568 errdefer allocator.free(version);
569
570 return Windows81Sdk{ .path = path, .version = version };
571 }
572
573 fn free(self: *const Windows81Sdk, allocator: std.mem.Allocator) void {
574 allocator.free(self.path);
575 allocator.free(self.version);
576 }
577};
578
579const MsvcLibDir = struct {
580 fn findInstancesDirViaCLSID(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {
581 const setup_configuration_clsid = "{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}";
582 const setup_config_key = RegistryWtf8.openKey(windows.HKEY_CLASSES_ROOT, "CLSID\\" ++ setup_configuration_clsid) catch |err| switch (err) {
583 error.KeyNotFound => return error.PathNotFound,
584 };
585 defer setup_config_key.closeKey();
586
587 const dll_path = setup_config_key.getString(allocator, "InprocServer32", "") catch |err| switch (err) {
588 error.NotAString,
589 error.ValueNameNotFound,
590 error.StringNotFound,
591 => return error.PathNotFound,
592
593 error.OutOfMemory => return error.OutOfMemory,
594 };
595 defer allocator.free(dll_path);
596
597 var path_it = std.fs.path.componentIterator(dll_path) catch return error.PathNotFound;
598 // the .dll filename
599 _ = path_it.last();
600 const root_path = while (path_it.previous()) |dir_component| {
601 if (std.ascii.eqlIgnoreCase(dir_component.name, "VisualStudio")) {
602 break dir_component.path;
603 }
604 } else {
605 return error.PathNotFound;
606 };
607
608 const instances_path = try std.fs.path.join(allocator, &.{ root_path, "Packages", "_Instances" });
609 defer allocator.free(instances_path);
610
611 return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return error.PathNotFound;
612 }
613
614 fn findInstancesDir(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {
615 // First try to get the path from the .dll that would have been
616 // loaded via COM for SetupConfiguration.
617 return findInstancesDirViaCLSID(allocator) catch |orig_err| {
618 // If that can't be found, fall back to manually appending
619 // `Microsoft\VisualStudio\Packages\_Instances` to %PROGRAMDATA%
620 const program_data = std.process.getEnvVarOwned(allocator, "PROGRAMDATA") catch |err| switch (err) {
621 error.OutOfMemory => |e| return e,
622 else => return orig_err,
623 };
624 defer allocator.free(program_data);
625
626 const instances_path = try std.fs.path.join(allocator, &.{ program_data, "Microsoft", "VisualStudio", "Packages", "_Instances" });
627 defer allocator.free(instances_path);
628
629 return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return orig_err;
630 };
631 }
632
633 /// Intended to be equivalent to `ISetupHelper.ParseVersion`
634 /// Example: 17.4.33205.214 -> 0x0011000481b500d6
635 fn parseVersionQuad(version: []const u8) error{InvalidVersion}!u64 {
636 var it = std.mem.splitScalar(u8, version, '.');
637 const a = it.next() orelse return error.InvalidVersion;
638 const b = it.next() orelse return error.InvalidVersion;
639 const c = it.next() orelse return error.InvalidVersion;
640 const d = it.next() orelse return error.InvalidVersion;
641 if (it.next()) |_| return error.InvalidVersion;
642 var result: u64 = undefined;
643 var result_bytes = std.mem.asBytes(&result);
644
645 std.mem.writeInt(
646 u16,
647 result_bytes[0..2],
648 std.fmt.parseUnsigned(u16, d, 10) catch return error.InvalidVersion,
649 .little,
650 );
651 std.mem.writeInt(
652 u16,
653 result_bytes[2..4],
654 std.fmt.parseUnsigned(u16, c, 10) catch return error.InvalidVersion,
655 .little,
656 );
657 std.mem.writeInt(
658 u16,
659 result_bytes[4..6],
660 std.fmt.parseUnsigned(u16, b, 10) catch return error.InvalidVersion,
661 .little,
662 );
663 std.mem.writeInt(
664 u16,
665 result_bytes[6..8],
666 std.fmt.parseUnsigned(u16, a, 10) catch return error.InvalidVersion,
667 .little,
668 );
669
670 return result;
671 }
672
673 /// Intended to be equivalent to ISetupConfiguration.EnumInstances:
674 /// https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.setup.configuration
675 /// but without the use of COM in order to avoid a dependency on ole32.dll
676 ///
677 /// The logic in this function is intended to match what ISetupConfiguration does
678 /// under-the-hood, as verified using Procmon.
679 fn findViaCOM(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
680 // Typically `%PROGRAMDATA%\Microsoft\VisualStudio\Packages\_Instances`
681 // This will contain directories with names of instance IDs like 80a758ca,
682 // which will contain `state.json` files that have the version and
683 // installation directory.
684 var instances_dir = try findInstancesDir(allocator);
685 defer instances_dir.close();
686
687 var state_subpath_buf: [std.fs.MAX_NAME_BYTES + 32]u8 = undefined;
688 var latest_version_lib_dir = std.ArrayListUnmanaged(u8){};
689 errdefer latest_version_lib_dir.deinit(allocator);
690
691 var latest_version: u64 = 0;
692 var instances_dir_it = instances_dir.iterateAssumeFirstIteration();
693 while (instances_dir_it.next() catch return error.PathNotFound) |entry| {
694 if (entry.kind != .directory) continue;
695
696 var fbs = std.io.fixedBufferStream(&state_subpath_buf);
697 const writer = fbs.writer();
698
699 writer.writeAll(entry.name) catch unreachable;
700 writer.writeByte(std.fs.path.sep) catch unreachable;
701 writer.writeAll("state.json") catch unreachable;
702
703 const json_contents = instances_dir.readFileAlloc(allocator, fbs.getWritten(), std.math.maxInt(usize)) catch continue;
704 defer allocator.free(json_contents);
705
706 var parsed = std.json.parseFromSlice(std.json.Value, allocator, json_contents, .{}) catch continue;
707 defer parsed.deinit();
708
709 if (parsed.value != .object) continue;
710 const catalog_info = parsed.value.object.get("catalogInfo") orelse continue;
711 if (catalog_info != .object) continue;
712 const product_version_value = catalog_info.object.get("buildVersion") orelse continue;
713 if (product_version_value != .string) continue;
714 const product_version_text = product_version_value.string;
715 const parsed_version = parseVersionQuad(product_version_text) catch continue;
716
717 // We want to end up with the most recent version installed
718 if (parsed_version <= latest_version) continue;
719
720 const installation_path = parsed.value.object.get("installationPath") orelse continue;
721 if (installation_path != .string) continue;
722
723 const lib_dir_path = libDirFromInstallationPath(allocator, installation_path.string) catch |err| switch (err) {
724 error.OutOfMemory => |e| return e,
725 error.PathNotFound => continue,
726 };
727 defer allocator.free(lib_dir_path);
728
729 latest_version_lib_dir.clearRetainingCapacity();
730 try latest_version_lib_dir.appendSlice(allocator, lib_dir_path);
731 latest_version = parsed_version;
732 }
733
734 if (latest_version_lib_dir.items.len == 0) return error.PathNotFound;
735 return latest_version_lib_dir.toOwnedSlice(allocator);
736 }
737
738 fn libDirFromInstallationPath(allocator: std.mem.Allocator, installation_path: []const u8) error{ OutOfMemory, PathNotFound }![]const u8 {
739 var lib_dir_buf = try std.ArrayList(u8).initCapacity(allocator, installation_path.len + 64);
740 errdefer lib_dir_buf.deinit();
741
742 lib_dir_buf.appendSliceAssumeCapacity(installation_path);
743
744 if (!std.fs.path.isSep(lib_dir_buf.getLast())) {
745 try lib_dir_buf.append('\\');
746 }
747 const installation_path_with_trailing_sep_len = lib_dir_buf.items.len;
748
749 try lib_dir_buf.appendSlice("VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
750 var default_tools_version_buf: [512]u8 = undefined;
751 const default_tools_version_contents = std.fs.cwd().readFile(lib_dir_buf.items, &default_tools_version_buf) catch {
752 return error.PathNotFound;
753 };
754 var tokenizer = std.mem.tokenizeAny(u8, default_tools_version_contents, " \r\n");
755 const default_tools_version = tokenizer.next() orelse return error.PathNotFound;
756
757 lib_dir_buf.shrinkRetainingCapacity(installation_path_with_trailing_sep_len);
758 try lib_dir_buf.appendSlice("VC\\Tools\\MSVC\\");
759 try lib_dir_buf.appendSlice(default_tools_version);
760 const folder_with_arch = "\\Lib\\" ++ comptime switch (builtin.target.cpu.arch) {
761 .x86 => "x86",
762 .x86_64 => "x64",
763 .arm, .armeb => "arm",
764 .aarch64 => "arm64",
765 else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag),
766 };
767 try lib_dir_buf.appendSlice(folder_with_arch);
768
769 if (!verifyLibDir(lib_dir_buf.items)) {
770 return error.PathNotFound;
771 }
772
773 return lib_dir_buf.toOwnedSlice();
774 }
775
776 // 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
777 fn findViaRegistry(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
778
779 // %localappdata%\Microsoft\VisualStudio\
780 // %appdata%\Local\Microsoft\VisualStudio\
781 const visualstudio_folder_path = std.fs.getAppDataDir(allocator, "Microsoft\\VisualStudio\\") catch return error.PathNotFound;
782 defer allocator.free(visualstudio_folder_path);
783
784 const vs_versions: []const []const u8 = vs_versions: {
785 if (!std.fs.path.isAbsolute(visualstudio_folder_path)) return error.PathNotFound;
786 // enumerate folders that contain `privateregistry.bin`, looking for all versions
787 // f.i. %localappdata%\Microsoft\VisualStudio\17.0_9e9cbb98\
788 var visualstudio_folder = std.fs.openDirAbsolute(visualstudio_folder_path, .{
789 .iterate = true,
790 }) catch return error.PathNotFound;
791 defer visualstudio_folder.close();
792
793 var iterator = visualstudio_folder.iterate();
794 const versions = iterateAndFilterBySemVer(&iterator, allocator, null) catch |err| switch (err) {
795 error.OutOfMemory => return error.OutOfMemory,
796 error.VersionNotFound => return error.PathNotFound,
797 };
798 break :vs_versions versions;
799 };
800 defer {
801 for (vs_versions) |vs_version| allocator.free(vs_version);
802 allocator.free(vs_versions);
803 }
804 var config_subkey_buf: [RegistryWtf16Le.key_name_max_len * 2]u8 = undefined;
805 const source_directories: []const u8 = source_directories: for (vs_versions) |vs_version| {
806 const privateregistry_absolute_path = std.fs.path.join(allocator, &.{ visualstudio_folder_path, vs_version, "privateregistry.bin" }) catch continue;
807 defer allocator.free(privateregistry_absolute_path);
808 if (!std.fs.path.isAbsolute(privateregistry_absolute_path)) continue;
809
810 const visualstudio_registry = RegistryWtf8.loadFromPath(privateregistry_absolute_path) catch continue;
811 defer visualstudio_registry.closeKey();
812
813 const config_subkey = std.fmt.bufPrint(config_subkey_buf[0..], "Software\\Microsoft\\VisualStudio\\{s}_Config", .{vs_version}) catch unreachable;
814
815 const source_directories_value = visualstudio_registry.getString(allocator, config_subkey, "Source Directories") catch |err| switch (err) {
816 error.OutOfMemory => return error.OutOfMemory,
817 else => continue,
818 };
819 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
820 allocator.free(source_directories_value);
821 continue;
822 }
823
824 break :source_directories source_directories_value;
825 } else return error.PathNotFound;
826 defer allocator.free(source_directories);
827
828 var source_directories_splitted = std.mem.splitScalar(u8, source_directories, ';');
829
830 const msvc_dir: []const u8 = msvc_dir: {
831 const msvc_include_dir_maybe_with_trailing_slash = try allocator.dupe(u8, source_directories_splitted.first());
832
833 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)) {
834 allocator.free(msvc_include_dir_maybe_with_trailing_slash);
835 return error.PathNotFound;
836 }
837
838 var msvc_dir = std.ArrayList(u8).fromOwnedSlice(allocator, msvc_include_dir_maybe_with_trailing_slash);
839 errdefer msvc_dir.deinit();
840
841 // String might contain trailing slash, so trim it here
842 if (msvc_dir.items.len > "C:\\".len and msvc_dir.getLast() == '\\') _ = msvc_dir.pop();
843
844 // Remove `\include` at the end of path
845 if (std.mem.endsWith(u8, msvc_dir.items, "\\include")) {
846 msvc_dir.shrinkRetainingCapacity(msvc_dir.items.len - "\\include".len);
847 }
848
849 const folder_with_arch = "\\Lib\\" ++ comptime switch (builtin.target.cpu.arch) {
850 .x86 => "x86",
851 .x86_64 => "x64",
852 .arm, .armeb => "arm",
853 .aarch64 => "arm64",
854 else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag),
855 };
856
857 try msvc_dir.appendSlice(folder_with_arch);
858 const msvc_dir_with_arch = try msvc_dir.toOwnedSlice();
859 break :msvc_dir msvc_dir_with_arch;
860 };
861 errdefer allocator.free(msvc_dir);
862
863 if (!verifyLibDir(msvc_dir)) {
864 return error.PathNotFound;
865 }
866
867 return msvc_dir;
868 }
869
870 fn findViaVs7Key(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
871 var base_path: std.ArrayList(u8) = base_path: {
872 try_env: {
873 var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
874 error.OutOfMemory => return error.OutOfMemory,
875 else => break :try_env,
876 };
877 defer env_map.deinit();
878
879 if (env_map.get("VS140COMNTOOLS")) |VS140COMNTOOLS| {
880 if (VS140COMNTOOLS.len < "C:\\Common7\\Tools".len) break :try_env;
881 if (!std.fs.path.isAbsolute(VS140COMNTOOLS)) break :try_env;
882 var list = std.ArrayList(u8).init(allocator);
883 errdefer list.deinit();
884
885 try list.appendSlice(VS140COMNTOOLS); // C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools
886 // String might contain trailing slash, so trim it here
887 if (list.items.len > "C:\\".len and list.getLast() == '\\') _ = list.pop();
888 list.shrinkRetainingCapacity(list.items.len - "\\Common7\\Tools".len); // C:\Program Files (x86)\Microsoft Visual Studio 14.0
889 break :base_path list;
890 }
891 }
892
893 const vs7_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7") catch return error.PathNotFound;
894 defer vs7_key.closeKey();
895 try_vs7_key: {
896 const path_maybe_with_trailing_slash = vs7_key.getString(allocator, "", "14.0") catch |err| switch (err) {
897 error.OutOfMemory => return error.OutOfMemory,
898 else => break :try_vs7_key,
899 };
900
901 if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
902 allocator.free(path_maybe_with_trailing_slash);
903 break :try_vs7_key;
904 }
905
906 var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
907 errdefer path.deinit();
908
909 // String might contain trailing slash, so trim it here
910 if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
911 break :base_path path;
912 }
913 return error.PathNotFound;
914 };
915 errdefer base_path.deinit();
916
917 const folder_with_arch = "\\VC\\lib\\" ++ comptime switch (builtin.target.cpu.arch) {
918 .x86 => "", //x86 is in the root of the Lib folder
919 .x86_64 => "amd64",
920 .arm, .armeb => "arm",
921 .aarch64 => "arm64",
922 else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag),
923 };
924 try base_path.appendSlice(folder_with_arch);
925
926 if (!verifyLibDir(base_path.items)) {
927 return error.PathNotFound;
928 }
929
930 const full_path = try base_path.toOwnedSlice();
931 return full_path;
932 }
933
934 fn verifyLibDir(lib_dir_path: []const u8) bool {
935 std.debug.assert(std.fs.path.isAbsolute(lib_dir_path)); // should be already handled in `findVia*`
936
937 var dir = std.fs.openDirAbsolute(lib_dir_path, .{}) catch return false;
938 defer dir.close();
939
940 const stat = dir.statFile("vcruntime.lib") catch return false;
941 if (stat.kind != .file)
942 return false;
943
944 return true;
945 }
946
947 /// Find path to MSVC's `lib/` directory.
948 /// Caller owns the result.
949 pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, MsvcLibDirNotFound }![]const u8 {
950 const full_path = MsvcLibDir.findViaCOM(allocator) catch |err1| switch (err1) {
951 error.OutOfMemory => return error.OutOfMemory,
952 error.PathNotFound => MsvcLibDir.findViaRegistry(allocator) catch |err2| switch (err2) {
953 error.OutOfMemory => return error.OutOfMemory,
954 error.PathNotFound => MsvcLibDir.findViaVs7Key(allocator) catch |err3| switch (err3) {
955 error.OutOfMemory => return error.OutOfMemory,
956 error.PathNotFound => return error.MsvcLibDirNotFound,
957 },
958 },
959 };
960 errdefer allocator.free(full_path);
961
962 return full_path;
963 }
964};
lib/std/zig/target.zig created+117
......@@ -0,0 +1,117 @@
1pub const ArchOsAbi = struct {
2 arch: std.Target.Cpu.Arch,
3 os: std.Target.Os.Tag,
4 abi: std.Target.Abi,
5 os_ver: ?std.SemanticVersion = null,
6
7 // Minimum glibc version that provides support for the arch/os when ABI is GNU.
8 glibc_min: ?std.SemanticVersion = null,
9};
10
11pub const available_libcs = [_]ArchOsAbi{
12 .{ .arch = .aarch64_be, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 17, .patch = 0 } },
13 .{ .arch = .aarch64_be, .os = .linux, .abi = .musl },
14 .{ .arch = .aarch64_be, .os = .windows, .abi = .gnu },
15 .{ .arch = .aarch64, .os = .linux, .abi = .gnu },
16 .{ .arch = .aarch64, .os = .linux, .abi = .musl },
17 .{ .arch = .aarch64, .os = .windows, .abi = .gnu },
18 .{ .arch = .aarch64, .os = .macos, .abi = .none, .os_ver = .{ .major = 11, .minor = 0, .patch = 0 } },
19 .{ .arch = .armeb, .os = .linux, .abi = .gnueabi },
20 .{ .arch = .armeb, .os = .linux, .abi = .gnueabihf },
21 .{ .arch = .armeb, .os = .linux, .abi = .musleabi },
22 .{ .arch = .armeb, .os = .linux, .abi = .musleabihf },
23 .{ .arch = .armeb, .os = .windows, .abi = .gnu },
24 .{ .arch = .arm, .os = .linux, .abi = .gnueabi },
25 .{ .arch = .arm, .os = .linux, .abi = .gnueabihf },
26 .{ .arch = .arm, .os = .linux, .abi = .musleabi },
27 .{ .arch = .arm, .os = .linux, .abi = .musleabihf },
28 .{ .arch = .thumb, .os = .linux, .abi = .gnueabi },
29 .{ .arch = .thumb, .os = .linux, .abi = .gnueabihf },
30 .{ .arch = .thumb, .os = .linux, .abi = .musleabi },
31 .{ .arch = .thumb, .os = .linux, .abi = .musleabihf },
32 .{ .arch = .arm, .os = .windows, .abi = .gnu },
33 .{ .arch = .csky, .os = .linux, .abi = .gnueabi },
34 .{ .arch = .csky, .os = .linux, .abi = .gnueabihf },
35 .{ .arch = .x86, .os = .linux, .abi = .gnu },
36 .{ .arch = .x86, .os = .linux, .abi = .musl },
37 .{ .arch = .x86, .os = .windows, .abi = .gnu },
38 .{ .arch = .m68k, .os = .linux, .abi = .gnu },
39 .{ .arch = .m68k, .os = .linux, .abi = .musl },
40 .{ .arch = .mips64el, .os = .linux, .abi = .gnuabi64 },
41 .{ .arch = .mips64el, .os = .linux, .abi = .gnuabin32 },
42 .{ .arch = .mips64el, .os = .linux, .abi = .musl },
43 .{ .arch = .mips64, .os = .linux, .abi = .gnuabi64 },
44 .{ .arch = .mips64, .os = .linux, .abi = .gnuabin32 },
45 .{ .arch = .mips64, .os = .linux, .abi = .musl },
46 .{ .arch = .mipsel, .os = .linux, .abi = .gnueabi },
47 .{ .arch = .mipsel, .os = .linux, .abi = .gnueabihf },
48 .{ .arch = .mipsel, .os = .linux, .abi = .musl },
49 .{ .arch = .mips, .os = .linux, .abi = .gnueabi },
50 .{ .arch = .mips, .os = .linux, .abi = .gnueabihf },
51 .{ .arch = .mips, .os = .linux, .abi = .musl },
52 .{ .arch = .powerpc64le, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 19, .patch = 0 } },
53 .{ .arch = .powerpc64le, .os = .linux, .abi = .musl },
54 .{ .arch = .powerpc64, .os = .linux, .abi = .gnu },
55 .{ .arch = .powerpc64, .os = .linux, .abi = .musl },
56 .{ .arch = .powerpc, .os = .linux, .abi = .gnueabi },
57 .{ .arch = .powerpc, .os = .linux, .abi = .gnueabihf },
58 .{ .arch = .powerpc, .os = .linux, .abi = .musl },
59 .{ .arch = .riscv64, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 27, .patch = 0 } },
60 .{ .arch = .riscv64, .os = .linux, .abi = .musl },
61 .{ .arch = .s390x, .os = .linux, .abi = .gnu },
62 .{ .arch = .s390x, .os = .linux, .abi = .musl },
63 .{ .arch = .sparc, .os = .linux, .abi = .gnu },
64 .{ .arch = .sparc64, .os = .linux, .abi = .gnu },
65 .{ .arch = .wasm32, .os = .freestanding, .abi = .musl },
66 .{ .arch = .wasm32, .os = .wasi, .abi = .musl },
67 .{ .arch = .x86_64, .os = .linux, .abi = .gnu },
68 .{ .arch = .x86_64, .os = .linux, .abi = .gnux32 },
69 .{ .arch = .x86_64, .os = .linux, .abi = .musl },
70 .{ .arch = .x86_64, .os = .windows, .abi = .gnu },
71 .{ .arch = .x86_64, .os = .macos, .abi = .none, .os_ver = .{ .major = 10, .minor = 7, .patch = 0 } },
72};
73
74pub fn canBuildLibC(target: std.Target) bool {
75 for (available_libcs) |libc| {
76 if (target.cpu.arch == libc.arch and target.os.tag == libc.os and target.abi == libc.abi) {
77 if (target.os.tag == .macos) {
78 const ver = target.os.version_range.semver;
79 return ver.min.order(libc.os_ver.?) != .lt;
80 }
81 // Ensure glibc (aka *-linux-gnu) version is supported
82 if (target.isGnuLibC()) {
83 const min_glibc_ver = libc.glibc_min orelse return true;
84 const target_glibc_ver = target.os.version_range.linux.glibc;
85 return target_glibc_ver.order(min_glibc_ver) != .lt;
86 }
87 return true;
88 }
89 }
90 return false;
91}
92
93pub fn muslArchNameHeaders(arch: std.Target.Cpu.Arch) [:0]const u8 {
94 return switch (arch) {
95 .x86 => return "x86",
96 else => muslArchName(arch),
97 };
98}
99
100pub fn muslArchName(arch: std.Target.Cpu.Arch) [:0]const u8 {
101 switch (arch) {
102 .aarch64, .aarch64_be => return "aarch64",
103 .arm, .armeb, .thumb, .thumbeb => return "arm",
104 .x86 => return "i386",
105 .mips, .mipsel => return "mips",
106 .mips64el, .mips64 => return "mips64",
107 .powerpc => return "powerpc",
108 .powerpc64, .powerpc64le => return "powerpc64",
109 .riscv64 => return "riscv64",
110 .s390x => return "s390x",
111 .wasm32, .wasm64 => return "wasm",
112 .x86_64 => return "x86_64",
113 else => unreachable,
114 }
115}
116
117const std = @import("std");
src/Compilation.zig+10-228
......@@ -19,7 +19,7 @@ const link = @import("link.zig");
1919const tracy = @import("tracy.zig");
2020const trace = tracy.trace;
2121const build_options = @import("build_options");
22const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
22const LibCInstallation = std.zig.LibCInstallation;
2323const glibc = @import("glibc.zig");
2424const musl = @import("musl.zig");
2525const mingw = @import("mingw.zig");
......@@ -1232,7 +1232,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
12321232
12331233 const link_libc = options.config.link_libc;
12341234
1235 const libc_dirs = try detectLibCIncludeDirs(
1235 const libc_dirs = try std.zig.LibCDirs.detect(
12361236 arena,
12371237 options.zig_lib_directory.path.?,
12381238 options.root_mod.resolved_target.result,
......@@ -1250,7 +1250,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
12501250 // only relevant differences would be things like `#define` constants being
12511251 // different in the MinGW headers vs the MSVC headers, but any such
12521252 // differences would likely be a MinGW bug.
1253 const rc_dirs = b: {
1253 const rc_dirs: std.zig.LibCDirs = b: {
12541254 // Set the includes to .none here when there are no rc files to compile
12551255 var includes = if (options.rc_source_files.len > 0) options.rc_includes else .none;
12561256 const target = options.root_mod.resolved_target.result;
......@@ -1265,7 +1265,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
12651265 }
12661266 }
12671267 while (true) switch (includes) {
1268 .any, .msvc => break :b detectLibCIncludeDirs(
1268 .any, .msvc => break :b std.zig.LibCDirs.detect(
12691269 arena,
12701270 options.zig_lib_directory.path.?,
12711271 .{
......@@ -1287,13 +1287,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
12871287 }
12881288 return err;
12891289 },
1290 .gnu => break :b try detectLibCFromBuilding(arena, options.zig_lib_directory.path.?, .{
1290 .gnu => break :b try std.zig.LibCDirs.detectFromBuilding(arena, options.zig_lib_directory.path.?, .{
12911291 .cpu = target.cpu,
12921292 .os = target.os,
12931293 .abi = .gnu,
12941294 .ofmt = target.ofmt,
12951295 }),
1296 .none => break :b LibCDirs{
1296 .none => break :b .{
12971297 .libc_include_dir_list = &[0][]u8{},
12981298 .libc_installation = null,
12991299 .libc_framework_dir_list = &.{},
......@@ -1772,7 +1772,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
17721772 // If we need to build glibc for the target, add work items for it.
17731773 // We go through the work queue so that building can be done in parallel.
17741774 if (comp.wantBuildGLibCFromSource()) {
1775 if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
1775 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
17761776
17771777 if (glibc.needsCrtiCrtn(target)) {
17781778 try comp.work_queue.write(&[_]Job{
......@@ -1787,7 +1787,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
17871787 });
17881788 }
17891789 if (comp.wantBuildMuslFromSource()) {
1790 if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
1790 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
17911791
17921792 try comp.work_queue.ensureUnusedCapacity(6);
17931793 if (musl.needsCrtiCrtn(target)) {
......@@ -1808,7 +1808,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
18081808 }
18091809
18101810 if (comp.wantBuildWasiLibcFromSource()) {
1811 if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
1811 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
18121812
18131813 // worst-case we need all components
18141814 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
18251825 }
18261826
18271827 if (comp.wantBuildMinGWFromSource()) {
1828 if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
1828 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
18291829
18301830 const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o };
18311831 try comp.work_queue.ensureUnusedCapacity(2);
......@@ -5830,224 +5830,6 @@ test "classifyFileExt" {
58305830 try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig"));
58315831}
58325832
5833const LibCDirs = struct {
5834 libc_include_dir_list: []const []const u8,
5835 libc_installation: ?*const LibCInstallation,
5836 libc_framework_dir_list: []const []const u8,
5837 sysroot: ?[]const u8,
5838 darwin_sdk_layout: ?link.File.MachO.SdkLayout,
5839};
5840
5841fn getZigShippedLibCIncludeDirsDarwin(arena: Allocator, zig_lib_dir: []const u8) !LibCDirs {
5842 const s = std.fs.path.sep_str;
5843 const list = try arena.alloc([]const u8, 1);
5844 list[0] = try std.fmt.allocPrint(
5845 arena,
5846 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-macos-any",
5847 .{zig_lib_dir},
5848 );
5849 return LibCDirs{
5850 .libc_include_dir_list = list,
5851 .libc_installation = null,
5852 .libc_framework_dir_list = &.{},
5853 .sysroot = null,
5854 .darwin_sdk_layout = .vendored,
5855 };
5856}
5857
5858pub fn detectLibCIncludeDirs(
5859 arena: Allocator,
5860 zig_lib_dir: []const u8,
5861 target: Target,
5862 is_native_abi: bool,
5863 link_libc: bool,
5864 libc_installation: ?*const LibCInstallation,
5865) !LibCDirs {
5866 if (!link_libc) {
5867 return LibCDirs{
5868 .libc_include_dir_list = &[0][]u8{},
5869 .libc_installation = null,
5870 .libc_framework_dir_list = &.{},
5871 .sysroot = null,
5872 .darwin_sdk_layout = null,
5873 };
5874 }
5875
5876 if (libc_installation) |lci| {
5877 return detectLibCFromLibCInstallation(arena, target, lci);
5878 }
5879
5880 // If linking system libraries and targeting the native abi, default to
5881 // using the system libc installation.
5882 if (is_native_abi and !target.isMinGW()) {
5883 const libc = try arena.create(LibCInstallation);
5884 libc.* = LibCInstallation.findNative(.{ .allocator = arena, .target = target }) catch |err| switch (err) {
5885 error.CCompilerExitCode,
5886 error.CCompilerCrashed,
5887 error.CCompilerCannotFindHeaders,
5888 error.UnableToSpawnCCompiler,
5889 error.DarwinSdkNotFound,
5890 => |e| {
5891 // We tried to integrate with the native system C compiler,
5892 // however, it is not installed. So we must rely on our bundled
5893 // libc files.
5894 if (target_util.canBuildLibC(target)) {
5895 return detectLibCFromBuilding(arena, zig_lib_dir, target);
5896 }
5897 return e;
5898 },
5899 else => |e| return e,
5900 };
5901 return detectLibCFromLibCInstallation(arena, target, libc);
5902 }
5903
5904 // If not linking system libraries, build and provide our own libc by
5905 // default if possible.
5906 if (target_util.canBuildLibC(target)) {
5907 return detectLibCFromBuilding(arena, zig_lib_dir, target);
5908 }
5909
5910 // If zig can't build the libc for the target and we are targeting the
5911 // native abi, fall back to using the system libc installation.
5912 // On windows, instead of the native (mingw) abi, we want to check
5913 // for the MSVC abi as a fallback.
5914 const use_system_abi = if (builtin.target.os.tag == .windows)
5915 target.abi == .msvc
5916 else
5917 is_native_abi;
5918
5919 if (use_system_abi) {
5920 const libc = try arena.create(LibCInstallation);
5921 libc.* = try LibCInstallation.findNative(.{ .allocator = arena, .verbose = true, .target = target });
5922 return detectLibCFromLibCInstallation(arena, target, libc);
5923 }
5924
5925 return LibCDirs{
5926 .libc_include_dir_list = &[0][]u8{},
5927 .libc_installation = null,
5928 .libc_framework_dir_list = &.{},
5929 .sysroot = null,
5930 .darwin_sdk_layout = null,
5931 };
5932}
5933
5934fn detectLibCFromLibCInstallation(arena: Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs {
5935 var list = try std.ArrayList([]const u8).initCapacity(arena, 5);
5936 var framework_list = std.ArrayList([]const u8).init(arena);
5937
5938 list.appendAssumeCapacity(lci.include_dir.?);
5939
5940 const is_redundant = mem.eql(u8, lci.sys_include_dir.?, lci.include_dir.?);
5941 if (!is_redundant) list.appendAssumeCapacity(lci.sys_include_dir.?);
5942
5943 if (target.os.tag == .windows) {
5944 if (std.fs.path.dirname(lci.sys_include_dir.?)) |sys_include_dir_parent| {
5945 // This include path will only exist when the optional "Desktop development with C++"
5946 // is installed. It contains headers, .rc files, and resources. It is especially
5947 // necessary when working with Windows resources.
5948 const atlmfc_dir = try std.fs.path.join(arena, &[_][]const u8{ sys_include_dir_parent, "atlmfc", "include" });
5949 list.appendAssumeCapacity(atlmfc_dir);
5950 }
5951 if (std.fs.path.dirname(lci.include_dir.?)) |include_dir_parent| {
5952 const um_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "um" });
5953 list.appendAssumeCapacity(um_dir);
5954
5955 const shared_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "shared" });
5956 list.appendAssumeCapacity(shared_dir);
5957 }
5958 }
5959 if (target.os.tag == .haiku) {
5960 const include_dir_path = lci.include_dir orelse return error.LibCInstallationNotAvailable;
5961 const os_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os" });
5962 list.appendAssumeCapacity(os_dir);
5963 // Errors.h
5964 const os_support_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os/support" });
5965 list.appendAssumeCapacity(os_support_dir);
5966
5967 const config_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "config" });
5968 list.appendAssumeCapacity(config_dir);
5969 }
5970
5971 var sysroot: ?[]const u8 = null;
5972
5973 if (target.isDarwin()) d: {
5974 const down1 = std.fs.path.dirname(lci.sys_include_dir.?) orelse break :d;
5975 const down2 = std.fs.path.dirname(down1) orelse break :d;
5976 try framework_list.append(try std.fs.path.join(arena, &.{ down2, "System", "Library", "Frameworks" }));
5977 sysroot = down2;
5978 }
5979
5980 return LibCDirs{
5981 .libc_include_dir_list = list.items,
5982 .libc_installation = lci,
5983 .libc_framework_dir_list = framework_list.items,
5984 .sysroot = sysroot,
5985 .darwin_sdk_layout = if (sysroot == null) null else .sdk,
5986 };
5987}
5988
5989fn detectLibCFromBuilding(
5990 arena: Allocator,
5991 zig_lib_dir: []const u8,
5992 target: std.Target,
5993) !LibCDirs {
5994 if (target.isDarwin())
5995 return getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir);
5996
5997 const generic_name = target_util.libCGenericName(target);
5998 // Some architectures are handled by the same set of headers.
5999 const arch_name = if (target.abi.isMusl())
6000 musl.archNameHeaders(target.cpu.arch)
6001 else if (target.cpu.arch.isThumb())
6002 // ARM headers are valid for Thumb too.
6003 switch (target.cpu.arch) {
6004 .thumb => "arm",
6005 .thumbeb => "armeb",
6006 else => unreachable,
6007 }
6008 else
6009 @tagName(target.cpu.arch);
6010 const os_name = @tagName(target.os.tag);
6011 // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name.
6012 const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi);
6013 const s = std.fs.path.sep_str;
6014 const arch_include_dir = try std.fmt.allocPrint(
6015 arena,
6016 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}",
6017 .{ zig_lib_dir, arch_name, os_name, abi_name },
6018 );
6019 const generic_include_dir = try std.fmt.allocPrint(
6020 arena,
6021 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}",
6022 .{ zig_lib_dir, generic_name },
6023 );
6024 const generic_arch_name = target_util.osArchName(target);
6025 const arch_os_include_dir = try std.fmt.allocPrint(
6026 arena,
6027 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any",
6028 .{ zig_lib_dir, generic_arch_name, os_name },
6029 );
6030 const generic_os_include_dir = try std.fmt.allocPrint(
6031 arena,
6032 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any",
6033 .{ zig_lib_dir, os_name },
6034 );
6035
6036 const list = try arena.alloc([]const u8, 4);
6037 list[0] = arch_include_dir;
6038 list[1] = generic_include_dir;
6039 list[2] = arch_os_include_dir;
6040 list[3] = generic_os_include_dir;
6041
6042 return LibCDirs{
6043 .libc_include_dir_list = list,
6044 .libc_installation = null,
6045 .libc_framework_dir_list = &.{},
6046 .sysroot = null,
6047 .darwin_sdk_layout = .vendored,
6048 };
6049}
6050
60515833pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 {
60525834 if (comp.wantBuildGLibCFromSource() or
60535835 comp.wantBuildMuslFromSource() or
src/glibc.zig+4-5
......@@ -7,7 +7,6 @@ const path = fs.path;
77const assert = std.debug.assert;
88const Version = std.SemanticVersion;
99
10const target_util = @import("target.zig");
1110const Compilation = @import("Compilation.zig");
1211const build_options = @import("build_options");
1312const trace = @import("tracy.zig").trace;
......@@ -21,7 +20,7 @@ pub const Lib = struct {
2120
2221pub const ABI = struct {
2322 all_versions: []const Version, // all defined versions (one abilist from v2.0.0 up to current)
24 all_targets: []const target_util.ArchOsAbi,
23 all_targets: []const std.zig.target.ArchOsAbi,
2524 /// The bytes from the file verbatim, starting from the u16 number
2625 /// of function inclusions.
2726 inclusions: []const u8,
......@@ -103,7 +102,7 @@ pub fn loadMetaData(gpa: Allocator, contents: []const u8) LoadMetaDataError!*ABI
103102 const targets_len = contents[index];
104103 index += 1;
105104
106 const targets = try arena.alloc(target_util.ArchOsAbi, targets_len);
105 const targets = try arena.alloc(std.zig.target.ArchOsAbi, targets_len);
107106 var i: u8 = 0;
108107 while (i < targets.len) : (i += 1) {
109108 const target_name = mem.sliceTo(contents[index..], 0);
......@@ -512,7 +511,7 @@ fn add_include_dirs(comp: *Compilation, arena: Allocator, args: *std.ArrayList([
512511 try args.append("-I");
513512 try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "generic-glibc"));
514513
515 const arch_name = target_util.osArchName(target);
514 const arch_name = target.osArchName();
516515 try args.append("-I");
517516 try args.append(try std.fmt.allocPrint(arena, "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-linux-any", .{
518517 comp.zig_lib_directory.path.?, arch_name,
......@@ -726,7 +725,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: *std.Progress.Node) !vo
726725 break i;
727726 }
728727 } else {
729 unreachable; // target_util.available_libcs prevents us from getting here
728 unreachable; // std.zig.target.available_libcs prevents us from getting here
730729 };
731730
732731 const target_ver_index = for (metadata.all_versions, 0..) |ver, i| {
src/introspect.zig+3-41
......@@ -84,14 +84,14 @@ pub fn resolveGlobalCacheDir(allocator: mem.Allocator) ![]u8 {
8484 if (builtin.os.tag == .wasi)
8585 @compileError("on WASI the global cache dir must be resolved with preopens");
8686
87 if (try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(allocator)) |value| return value;
87 if (try std.zig.EnvVar.ZIG_GLOBAL_CACHE_DIR.get(allocator)) |value| return value;
8888
8989 const appname = "zig";
9090
9191 if (builtin.os.tag != .windows) {
92 if (EnvVar.XDG_CACHE_HOME.getPosix()) |cache_root| {
92 if (std.zig.EnvVar.XDG_CACHE_HOME.getPosix()) |cache_root| {
9393 return fs.path.join(allocator, &[_][]const u8{ cache_root, appname });
94 } else if (EnvVar.HOME.getPosix()) |home| {
94 } else if (std.zig.EnvVar.HOME.getPosix()) |home| {
9595 return fs.path.join(allocator, &[_][]const u8{ home, ".cache", appname });
9696 }
9797 }
......@@ -141,41 +141,3 @@ pub fn resolvePath(
141141pub fn isUpDir(p: []const u8) bool {
142142 return mem.startsWith(u8, p, "..") and (p.len == 2 or p[2] == fs.path.sep);
143143}
144
145/// Collects all the environment variables that Zig could possibly inspect, so
146/// that we can do reflection on this and print them with `zig env`.
147pub const EnvVar = enum {
148 ZIG_GLOBAL_CACHE_DIR,
149 ZIG_LOCAL_CACHE_DIR,
150 ZIG_LIB_DIR,
151 ZIG_LIBC,
152 ZIG_BUILD_RUNNER,
153 ZIG_VERBOSE_LINK,
154 ZIG_VERBOSE_CC,
155 ZIG_BTRFS_WORKAROUND,
156 ZIG_DEBUG_CMD,
157 CC,
158 NO_COLOR,
159 XDG_CACHE_HOME,
160 HOME,
161
162 pub fn isSet(comptime ev: EnvVar) bool {
163 return std.process.hasEnvVarConstant(@tagName(ev));
164 }
165
166 pub fn get(ev: EnvVar, arena: mem.Allocator) !?[]u8 {
167 // Env vars aren't used in the bootstrap stage.
168 if (build_options.only_c) return null;
169
170 if (std.process.getEnvVarOwned(arena, @tagName(ev))) |value| {
171 return value;
172 } else |err| switch (err) {
173 error.EnvironmentVariableNotFound => return null,
174 else => |e| return e,
175 }
176 }
177
178 pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 {
179 return std.os.getenvZ(@tagName(ev));
180 }
181};
src/libc_installation.zig deleted-712
......@@ -1,712 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const Target = std.Target;
4const fs = std.fs;
5const Allocator = std.mem.Allocator;
6
7const is_darwin = builtin.target.isDarwin();
8const is_windows = builtin.target.os.tag == .windows;
9const is_haiku = builtin.target.os.tag == .haiku;
10
11const log = std.log.scoped(.libc_installation);
12
13const ZigWindowsSDK = @import("windows_sdk.zig").ZigWindowsSDK;
14const EnvVar = @import("introspect.zig").EnvVar;
15
16/// See the render function implementation for documentation of the fields.
17pub const LibCInstallation = struct {
18 include_dir: ?[]const u8 = null,
19 sys_include_dir: ?[]const u8 = null,
20 crt_dir: ?[]const u8 = null,
21 msvc_lib_dir: ?[]const u8 = null,
22 kernel32_lib_dir: ?[]const u8 = null,
23 gcc_dir: ?[]const u8 = null,
24
25 pub const FindError = error{
26 OutOfMemory,
27 FileSystem,
28 UnableToSpawnCCompiler,
29 CCompilerExitCode,
30 CCompilerCrashed,
31 CCompilerCannotFindHeaders,
32 LibCRuntimeNotFound,
33 LibCStdLibHeaderNotFound,
34 LibCKernel32LibNotFound,
35 UnsupportedArchitecture,
36 WindowsSdkNotFound,
37 DarwinSdkNotFound,
38 ZigIsTheCCompiler,
39 };
40
41 pub fn parse(
42 allocator: Allocator,
43 libc_file: []const u8,
44 target: std.Target,
45 ) !LibCInstallation {
46 var self: LibCInstallation = .{};
47
48 const fields = std.meta.fields(LibCInstallation);
49 const FoundKey = struct {
50 found: bool,
51 allocated: ?[:0]u8,
52 };
53 var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** fields.len;
54 errdefer {
55 self = .{};
56 for (found_keys) |found_key| {
57 if (found_key.allocated) |s| allocator.free(s);
58 }
59 }
60
61 const contents = try std.fs.cwd().readFileAlloc(allocator, libc_file, std.math.maxInt(usize));
62 defer allocator.free(contents);
63
64 var it = std.mem.tokenizeScalar(u8, contents, '\n');
65 while (it.next()) |line| {
66 if (line.len == 0 or line[0] == '#') continue;
67 var line_it = std.mem.splitScalar(u8, line, '=');
68 const name = line_it.first();
69 const value = line_it.rest();
70 inline for (fields, 0..) |field, i| {
71 if (std.mem.eql(u8, name, field.name)) {
72 found_keys[i].found = true;
73 if (value.len == 0) {
74 @field(self, field.name) = null;
75 } else {
76 found_keys[i].allocated = try allocator.dupeZ(u8, value);
77 @field(self, field.name) = found_keys[i].allocated;
78 }
79 break;
80 }
81 }
82 }
83 inline for (fields, 0..) |field, i| {
84 if (!found_keys[i].found) {
85 log.err("missing field: {s}\n", .{field.name});
86 return error.ParseError;
87 }
88 }
89 if (self.include_dir == null) {
90 log.err("include_dir may not be empty\n", .{});
91 return error.ParseError;
92 }
93 if (self.sys_include_dir == null) {
94 log.err("sys_include_dir may not be empty\n", .{});
95 return error.ParseError;
96 }
97
98 const os_tag = target.os.tag;
99 if (self.crt_dir == null and !target.isDarwin()) {
100 log.err("crt_dir may not be empty for {s}\n", .{@tagName(os_tag)});
101 return error.ParseError;
102 }
103
104 if (self.msvc_lib_dir == null and os_tag == .windows and target.abi == .msvc) {
105 log.err("msvc_lib_dir may not be empty for {s}-{s}\n", .{
106 @tagName(os_tag),
107 @tagName(target.abi),
108 });
109 return error.ParseError;
110 }
111 if (self.kernel32_lib_dir == null and os_tag == .windows and target.abi == .msvc) {
112 log.err("kernel32_lib_dir may not be empty for {s}-{s}\n", .{
113 @tagName(os_tag),
114 @tagName(target.abi),
115 });
116 return error.ParseError;
117 }
118
119 if (self.gcc_dir == null and os_tag == .haiku) {
120 log.err("gcc_dir may not be empty for {s}\n", .{@tagName(os_tag)});
121 return error.ParseError;
122 }
123
124 return self;
125 }
126
127 pub fn render(self: LibCInstallation, out: anytype) !void {
128 @setEvalBranchQuota(4000);
129 const include_dir = self.include_dir orelse "";
130 const sys_include_dir = self.sys_include_dir orelse "";
131 const crt_dir = self.crt_dir orelse "";
132 const msvc_lib_dir = self.msvc_lib_dir orelse "";
133 const kernel32_lib_dir = self.kernel32_lib_dir orelse "";
134 const gcc_dir = self.gcc_dir orelse "";
135
136 try out.print(
137 \\# The directory that contains `stdlib.h`.
138 \\# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null`
139 \\include_dir={s}
140 \\
141 \\# The system-specific include directory. May be the same as `include_dir`.
142 \\# On Windows it's the directory that includes `vcruntime.h`.
143 \\# On POSIX it's the directory that includes `sys/errno.h`.
144 \\sys_include_dir={s}
145 \\
146 \\# The directory that contains `crt1.o` or `crt2.o`.
147 \\# On POSIX, can be found with `cc -print-file-name=crt1.o`.
148 \\# Not needed when targeting MacOS.
149 \\crt_dir={s}
150 \\
151 \\# The directory that contains `vcruntime.lib`.
152 \\# Only needed when targeting MSVC on Windows.
153 \\msvc_lib_dir={s}
154 \\
155 \\# The directory that contains `kernel32.lib`.
156 \\# Only needed when targeting MSVC on Windows.
157 \\kernel32_lib_dir={s}
158 \\
159 \\# The directory that contains `crtbeginS.o` and `crtendS.o`
160 \\# Only needed when targeting Haiku.
161 \\gcc_dir={s}
162 \\
163 , .{
164 include_dir,
165 sys_include_dir,
166 crt_dir,
167 msvc_lib_dir,
168 kernel32_lib_dir,
169 gcc_dir,
170 });
171 }
172
173 pub const FindNativeOptions = struct {
174 allocator: Allocator,
175 target: std.Target,
176
177 /// If enabled, will print human-friendly errors to stderr.
178 verbose: bool = false,
179 };
180
181 /// Finds the default, native libc.
182 pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation {
183 var self: LibCInstallation = .{};
184
185 if (is_darwin) {
186 if (!std.zig.system.darwin.isSdkInstalled(args.allocator))
187 return error.DarwinSdkNotFound;
188 const sdk = std.zig.system.darwin.getSdk(args.allocator, args.target) orelse
189 return error.DarwinSdkNotFound;
190 defer args.allocator.free(sdk);
191
192 self.include_dir = try fs.path.join(args.allocator, &.{
193 sdk, "usr/include",
194 });
195 self.sys_include_dir = try fs.path.join(args.allocator, &.{
196 sdk, "usr/include",
197 });
198 return self;
199 } else if (is_windows) {
200 var sdk: ZigWindowsSDK = ZigWindowsSDK.find(args.allocator) catch |err| switch (err) {
201 error.NotFound => return error.WindowsSdkNotFound,
202 error.PathTooLong => return error.WindowsSdkNotFound,
203 error.OutOfMemory => return error.OutOfMemory,
204 };
205 defer sdk.free(args.allocator);
206
207 try self.findNativeMsvcIncludeDir(args, &sdk);
208 try self.findNativeMsvcLibDir(args, &sdk);
209 try self.findNativeKernel32LibDir(args, &sdk);
210 try self.findNativeIncludeDirWindows(args, &sdk);
211 try self.findNativeCrtDirWindows(args, &sdk);
212 } else if (is_haiku) {
213 try self.findNativeIncludeDirPosix(args);
214 try self.findNativeCrtBeginDirHaiku(args);
215 self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib");
216 } else if (builtin.target.os.tag.isSolarish()) {
217 // There is only one libc, and its headers/libraries are always in the same spot.
218 self.include_dir = try args.allocator.dupeZ(u8, "/usr/include");
219 self.sys_include_dir = try args.allocator.dupeZ(u8, "/usr/include");
220 self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib/64");
221 } else if (std.process.can_spawn) {
222 try self.findNativeIncludeDirPosix(args);
223 switch (builtin.target.os.tag) {
224 .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib"),
225 .linux => try self.findNativeCrtDirPosix(args),
226 else => {},
227 }
228 } else {
229 return error.LibCRuntimeNotFound;
230 }
231 return self;
232 }
233
234 /// Must be the same allocator passed to `parse` or `findNative`.
235 pub fn deinit(self: *LibCInstallation, allocator: Allocator) void {
236 const fields = std.meta.fields(LibCInstallation);
237 inline for (fields) |field| {
238 if (@field(self, field.name)) |payload| {
239 allocator.free(payload);
240 }
241 }
242 self.* = undefined;
243 }
244
245 fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
246 const allocator = args.allocator;
247
248 // Detect infinite loops.
249 var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
250 error.Unexpected => unreachable, // WASI-only
251 else => |e| return e,
252 };
253 defer env_map.deinit();
254 const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: {
255 if (std.mem.eql(u8, phase, "1")) {
256 try env_map.put(inf_loop_env_key, "2");
257 break :blk true;
258 } else {
259 return error.ZigIsTheCCompiler;
260 }
261 } else blk: {
262 try env_map.put(inf_loop_env_key, "1");
263 break :blk false;
264 };
265
266 const dev_null = if (is_windows) "nul" else "/dev/null";
267
268 var argv = std.ArrayList([]const u8).init(allocator);
269 defer argv.deinit();
270
271 try appendCcExe(&argv, skip_cc_env_var);
272 try argv.appendSlice(&.{
273 "-E",
274 "-Wp,-v",
275 "-xc",
276 dev_null,
277 });
278
279 const run_res = std.ChildProcess.run(.{
280 .allocator = allocator,
281 .argv = argv.items,
282 .max_output_bytes = 1024 * 1024,
283 .env_map = &env_map,
284 // Some C compilers, such as Clang, are known to rely on argv[0] to find the path
285 // to their own executable, without even bothering to resolve PATH. This results in the message:
286 // error: unable to execute command: Executable "" doesn't exist!
287 // So we use the expandArg0 variant of ChildProcess to give them a helping hand.
288 .expand_arg0 = .expand,
289 }) catch |err| switch (err) {
290 error.OutOfMemory => return error.OutOfMemory,
291 else => {
292 printVerboseInvocation(argv.items, null, args.verbose, null);
293 return error.UnableToSpawnCCompiler;
294 },
295 };
296 defer {
297 allocator.free(run_res.stdout);
298 allocator.free(run_res.stderr);
299 }
300 switch (run_res.term) {
301 .Exited => |code| if (code != 0) {
302 printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr);
303 return error.CCompilerExitCode;
304 },
305 else => {
306 printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr);
307 return error.CCompilerCrashed;
308 },
309 }
310
311 var it = std.mem.tokenizeAny(u8, run_res.stderr, "\n\r");
312 var search_paths = std.ArrayList([]const u8).init(allocator);
313 defer search_paths.deinit();
314 while (it.next()) |line| {
315 if (line.len != 0 and line[0] == ' ') {
316 try search_paths.append(line);
317 }
318 }
319 if (search_paths.items.len == 0) {
320 return error.CCompilerCannotFindHeaders;
321 }
322
323 const include_dir_example_file = if (is_haiku) "posix/stdlib.h" else "stdlib.h";
324 const sys_include_dir_example_file = if (is_windows)
325 "sys\\types.h"
326 else if (is_haiku)
327 "errno.h"
328 else
329 "sys/errno.h";
330
331 var path_i: usize = 0;
332 while (path_i < search_paths.items.len) : (path_i += 1) {
333 // search in reverse order
334 const search_path_untrimmed = search_paths.items[search_paths.items.len - path_i - 1];
335 const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
336 var search_dir = fs.cwd().openDir(search_path, .{}) catch |err| switch (err) {
337 error.FileNotFound,
338 error.NotDir,
339 error.NoDevice,
340 => continue,
341
342 else => return error.FileSystem,
343 };
344 defer search_dir.close();
345
346 if (self.include_dir == null) {
347 if (search_dir.accessZ(include_dir_example_file, .{})) |_| {
348 self.include_dir = try allocator.dupeZ(u8, search_path);
349 } else |err| switch (err) {
350 error.FileNotFound => {},
351 else => return error.FileSystem,
352 }
353 }
354
355 if (self.sys_include_dir == null) {
356 if (search_dir.accessZ(sys_include_dir_example_file, .{})) |_| {
357 self.sys_include_dir = try allocator.dupeZ(u8, search_path);
358 } else |err| switch (err) {
359 error.FileNotFound => {},
360 else => return error.FileSystem,
361 }
362 }
363
364 if (self.include_dir != null and self.sys_include_dir != null) {
365 // Success.
366 return;
367 }
368 }
369
370 return error.LibCStdLibHeaderNotFound;
371 }
372
373 fn findNativeIncludeDirWindows(
374 self: *LibCInstallation,
375 args: FindNativeOptions,
376 sdk: *ZigWindowsSDK,
377 ) FindError!void {
378 const allocator = args.allocator;
379
380 var search_buf: [2]Search = undefined;
381 const searches = fillSearch(&search_buf, sdk);
382
383 var result_buf = std.ArrayList(u8).init(allocator);
384 defer result_buf.deinit();
385
386 for (searches) |search| {
387 result_buf.shrinkAndFree(0);
388 try result_buf.writer().print("{s}\\Include\\{s}\\ucrt", .{ search.path, search.version });
389
390 var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) {
391 error.FileNotFound,
392 error.NotDir,
393 error.NoDevice,
394 => continue,
395
396 else => return error.FileSystem,
397 };
398 defer dir.close();
399
400 dir.accessZ("stdlib.h", .{}) catch |err| switch (err) {
401 error.FileNotFound => continue,
402 else => return error.FileSystem,
403 };
404
405 self.include_dir = try result_buf.toOwnedSlice();
406 return;
407 }
408
409 return error.LibCStdLibHeaderNotFound;
410 }
411
412 fn findNativeCrtDirWindows(
413 self: *LibCInstallation,
414 args: FindNativeOptions,
415 sdk: *ZigWindowsSDK,
416 ) FindError!void {
417 const allocator = args.allocator;
418
419 var search_buf: [2]Search = undefined;
420 const searches = fillSearch(&search_buf, sdk);
421
422 var result_buf = std.ArrayList(u8).init(allocator);
423 defer result_buf.deinit();
424
425 const arch_sub_dir = switch (builtin.target.cpu.arch) {
426 .x86 => "x86",
427 .x86_64 => "x64",
428 .arm, .armeb => "arm",
429 .aarch64 => "arm64",
430 else => return error.UnsupportedArchitecture,
431 };
432
433 for (searches) |search| {
434 result_buf.shrinkAndFree(0);
435 try result_buf.writer().print("{s}\\Lib\\{s}\\ucrt\\{s}", .{ search.path, search.version, arch_sub_dir });
436
437 var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) {
438 error.FileNotFound,
439 error.NotDir,
440 error.NoDevice,
441 => continue,
442
443 else => return error.FileSystem,
444 };
445 defer dir.close();
446
447 dir.accessZ("ucrt.lib", .{}) catch |err| switch (err) {
448 error.FileNotFound => continue,
449 else => return error.FileSystem,
450 };
451
452 self.crt_dir = try result_buf.toOwnedSlice();
453 return;
454 }
455 return error.LibCRuntimeNotFound;
456 }
457
458 fn findNativeCrtDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
459 self.crt_dir = try ccPrintFileName(.{
460 .allocator = args.allocator,
461 .search_basename = "crt1.o",
462 .want_dirname = .only_dir,
463 .verbose = args.verbose,
464 });
465 }
466
467 fn findNativeCrtBeginDirHaiku(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
468 self.gcc_dir = try ccPrintFileName(.{
469 .allocator = args.allocator,
470 .search_basename = "crtbeginS.o",
471 .want_dirname = .only_dir,
472 .verbose = args.verbose,
473 });
474 }
475
476 fn findNativeKernel32LibDir(
477 self: *LibCInstallation,
478 args: FindNativeOptions,
479 sdk: *ZigWindowsSDK,
480 ) FindError!void {
481 const allocator = args.allocator;
482
483 var search_buf: [2]Search = undefined;
484 const searches = fillSearch(&search_buf, sdk);
485
486 var result_buf = std.ArrayList(u8).init(allocator);
487 defer result_buf.deinit();
488
489 const arch_sub_dir = switch (builtin.target.cpu.arch) {
490 .x86 => "x86",
491 .x86_64 => "x64",
492 .arm, .armeb => "arm",
493 .aarch64 => "arm64",
494 else => return error.UnsupportedArchitecture,
495 };
496
497 for (searches) |search| {
498 result_buf.shrinkAndFree(0);
499 const stream = result_buf.writer();
500 try stream.print("{s}\\Lib\\{s}\\um\\{s}", .{ search.path, search.version, arch_sub_dir });
501
502 var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) {
503 error.FileNotFound,
504 error.NotDir,
505 error.NoDevice,
506 => continue,
507
508 else => return error.FileSystem,
509 };
510 defer dir.close();
511
512 dir.accessZ("kernel32.lib", .{}) catch |err| switch (err) {
513 error.FileNotFound => continue,
514 else => return error.FileSystem,
515 };
516
517 self.kernel32_lib_dir = try result_buf.toOwnedSlice();
518 return;
519 }
520 return error.LibCKernel32LibNotFound;
521 }
522
523 fn findNativeMsvcIncludeDir(
524 self: *LibCInstallation,
525 args: FindNativeOptions,
526 sdk: *ZigWindowsSDK,
527 ) FindError!void {
528 const allocator = args.allocator;
529
530 const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCStdLibHeaderNotFound;
531 const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound;
532 const up2 = fs.path.dirname(up1) orelse return error.LibCStdLibHeaderNotFound;
533
534 const dir_path = try fs.path.join(allocator, &[_][]const u8{ up2, "include" });
535 errdefer allocator.free(dir_path);
536
537 var dir = fs.cwd().openDir(dir_path, .{}) catch |err| switch (err) {
538 error.FileNotFound,
539 error.NotDir,
540 error.NoDevice,
541 => return error.LibCStdLibHeaderNotFound,
542
543 else => return error.FileSystem,
544 };
545 defer dir.close();
546
547 dir.accessZ("vcruntime.h", .{}) catch |err| switch (err) {
548 error.FileNotFound => return error.LibCStdLibHeaderNotFound,
549 else => return error.FileSystem,
550 };
551
552 self.sys_include_dir = dir_path;
553 }
554
555 fn findNativeMsvcLibDir(
556 self: *LibCInstallation,
557 args: FindNativeOptions,
558 sdk: *ZigWindowsSDK,
559 ) FindError!void {
560 const allocator = args.allocator;
561 const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCRuntimeNotFound;
562 self.msvc_lib_dir = try allocator.dupe(u8, msvc_lib_dir);
563 }
564};
565
566pub const CCPrintFileNameOptions = struct {
567 allocator: Allocator,
568 search_basename: []const u8,
569 want_dirname: enum { full_path, only_dir },
570 verbose: bool = false,
571};
572
573/// caller owns returned memory
574fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 {
575 const allocator = args.allocator;
576
577 // Detect infinite loops.
578 var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
579 error.Unexpected => unreachable, // WASI-only
580 else => |e| return e,
581 };
582 defer env_map.deinit();
583 const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: {
584 if (std.mem.eql(u8, phase, "1")) {
585 try env_map.put(inf_loop_env_key, "2");
586 break :blk true;
587 } else {
588 return error.ZigIsTheCCompiler;
589 }
590 } else blk: {
591 try env_map.put(inf_loop_env_key, "1");
592 break :blk false;
593 };
594
595 var argv = std.ArrayList([]const u8).init(allocator);
596 defer argv.deinit();
597
598 const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={s}", .{args.search_basename});
599 defer allocator.free(arg1);
600
601 try appendCcExe(&argv, skip_cc_env_var);
602 try argv.append(arg1);
603
604 const run_res = std.ChildProcess.run(.{
605 .allocator = allocator,
606 .argv = argv.items,
607 .max_output_bytes = 1024 * 1024,
608 .env_map = &env_map,
609 // Some C compilers, such as Clang, are known to rely on argv[0] to find the path
610 // to their own executable, without even bothering to resolve PATH. This results in the message:
611 // error: unable to execute command: Executable "" doesn't exist!
612 // So we use the expandArg0 variant of ChildProcess to give them a helping hand.
613 .expand_arg0 = .expand,
614 }) catch |err| switch (err) {
615 error.OutOfMemory => return error.OutOfMemory,
616 else => return error.UnableToSpawnCCompiler,
617 };
618 defer {
619 allocator.free(run_res.stdout);
620 allocator.free(run_res.stderr);
621 }
622 switch (run_res.term) {
623 .Exited => |code| if (code != 0) {
624 printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr);
625 return error.CCompilerExitCode;
626 },
627 else => {
628 printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr);
629 return error.CCompilerCrashed;
630 },
631 }
632
633 var it = std.mem.tokenizeAny(u8, run_res.stdout, "\n\r");
634 const line = it.next() orelse return error.LibCRuntimeNotFound;
635 // When this command fails, it returns exit code 0 and duplicates the input file name.
636 // So we detect failure by checking if the output matches exactly the input.
637 if (std.mem.eql(u8, line, args.search_basename)) return error.LibCRuntimeNotFound;
638 switch (args.want_dirname) {
639 .full_path => return allocator.dupeZ(u8, line),
640 .only_dir => {
641 const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
642 return allocator.dupeZ(u8, dirname);
643 },
644 }
645}
646
647fn printVerboseInvocation(
648 argv: []const []const u8,
649 search_basename: ?[]const u8,
650 verbose: bool,
651 stderr: ?[]const u8,
652) void {
653 if (!verbose) return;
654
655 if (search_basename) |s| {
656 std.debug.print("Zig attempted to find the file '{s}' by executing this command:\n", .{s});
657 } else {
658 std.debug.print("Zig attempted to find the path to native system libc headers by executing this command:\n", .{});
659 }
660 for (argv, 0..) |arg, i| {
661 if (i != 0) std.debug.print(" ", .{});
662 std.debug.print("{s}", .{arg});
663 }
664 std.debug.print("\n", .{});
665 if (stderr) |s| {
666 std.debug.print("Output:\n==========\n{s}\n==========\n", .{s});
667 }
668}
669
670const Search = struct {
671 path: []const u8,
672 version: []const u8,
673};
674
675fn fillSearch(search_buf: *[2]Search, sdk: *ZigWindowsSDK) []Search {
676 var search_end: usize = 0;
677 if (sdk.windows10sdk) |windows10sdk| {
678 search_buf[search_end] = .{
679 .path = windows10sdk.path,
680 .version = windows10sdk.version,
681 };
682 search_end += 1;
683 }
684 if (sdk.windows81sdk) |windows81sdk| {
685 search_buf[search_end] = .{
686 .path = windows81sdk.path,
687 .version = windows81sdk.version,
688 };
689 search_end += 1;
690 }
691 return search_buf[0..search_end];
692}
693
694const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS";
695
696fn appendCcExe(args: *std.ArrayList([]const u8), skip_cc_env_var: bool) !void {
697 const default_cc_exe = if (is_windows) "cc.exe" else "cc";
698 try args.ensureUnusedCapacity(1);
699 if (skip_cc_env_var) {
700 args.appendAssumeCapacity(default_cc_exe);
701 return;
702 }
703 const cc_env_var = EnvVar.CC.getPosix() orelse {
704 args.appendAssumeCapacity(default_cc_exe);
705 return;
706 };
707 // Respect space-separated flags to the C compiler.
708 var it = std.mem.tokenizeScalar(u8, cc_env_var, ' ');
709 while (it.next()) |arg| {
710 try args.append(arg);
711 }
712}
src/link.zig+1-1
......@@ -12,7 +12,7 @@ const Air = @import("Air.zig");
1212const Allocator = std.mem.Allocator;
1313const Cache = std.Build.Cache;
1414const Compilation = @import("Compilation.zig");
15const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
15const LibCInstallation = std.zig.LibCInstallation;
1616const Liveness = @import("Liveness.zig");
1717const Module = @import("Module.zig");
1818const InternPool = @import("InternPool.zig");
src/link/MachO.zig+1-7
......@@ -4616,13 +4616,7 @@ const SystemLib = struct {
46164616 must_link: bool = false,
46174617};
46184618
4619/// The filesystem layout of darwin SDK elements.
4620pub const SdkLayout = enum {
4621 /// macOS SDK layout: TOP { /usr/include, /usr/lib, /System/Library/Frameworks }.
4622 sdk,
4623 /// Shipped libc layout: TOP { /lib/libc/include, /lib/libc/darwin, <NONE> }.
4624 vendored,
4625};
4619pub const SdkLayout = std.zig.LibCDirs.DarwinSdkLayout;
46264620
46274621const UndefinedTreatment = enum {
46284622 @"error",
src/main.zig+25-206
......@@ -19,8 +19,8 @@ const link = @import("link.zig");
1919const Package = @import("Package.zig");
2020const build_options = @import("build_options");
2121const introspect = @import("introspect.zig");
22const EnvVar = introspect.EnvVar;
23const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
22const EnvVar = std.zig.EnvVar;
23const LibCInstallation = std.zig.LibCInstallation;
2424const wasi_libc = @import("wasi_libc.zig");
2525const Cache = std.Build.Cache;
2626const target_util = @import("target.zig");
......@@ -294,17 +294,17 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
294294 } else if (mem.eql(u8, cmd, "rc")) {
295295 return cmdRc(gpa, arena, args[1..]);
296296 } else if (mem.eql(u8, cmd, "fmt")) {
297 return jitCmd(gpa, arena, cmd_args, "fmt", "fmt.zig");
297 return jitCmd(gpa, arena, cmd_args, "fmt", "fmt.zig", false);
298298 } else if (mem.eql(u8, cmd, "objcopy")) {
299299 return @import("objcopy.zig").cmdObjCopy(gpa, arena, cmd_args);
300300 } else if (mem.eql(u8, cmd, "fetch")) {
301301 return cmdFetch(gpa, arena, cmd_args);
302302 } else if (mem.eql(u8, cmd, "libc")) {
303 return cmdLibC(gpa, cmd_args);
303 return jitCmd(gpa, arena, cmd_args, "libc", "libc.zig", true);
304304 } else if (mem.eql(u8, cmd, "init")) {
305305 return cmdInit(gpa, arena, cmd_args);
306306 } else if (mem.eql(u8, cmd, "targets")) {
307 const host = resolveTargetQueryOrFatal(.{});
307 const host = std.zig.resolveTargetQueryOrFatal(.{});
308308 const stdout = io.getStdOut().writer();
309309 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, host);
310310 } else if (mem.eql(u8, cmd, "version")) {
......@@ -317,7 +317,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
317317 verifyLibcxxCorrectlyLinked();
318318 return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer());
319319 } else if (mem.eql(u8, cmd, "reduce")) {
320 return jitCmd(gpa, arena, cmd_args, "reduce", "reduce.zig");
320 return jitCmd(gpa, arena, cmd_args, "reduce", "reduce.zig", false);
321321 } else if (mem.eql(u8, cmd, "zen")) {
322322 return io.getStdOut().writeAll(info_zen);
323323 } 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(
32593259 const triple_name = try target.zigTriple(arena);
32603260 std.log.err("unable to find or provide libc for target '{s}'", .{triple_name});
32613261
3262 for (target_util.available_libcs) |t| {
3262 for (std.zig.target.available_libcs) |t| {
32633263 if (t.arch == target.cpu.arch and t.os == target.os.tag) {
32643264 if (t.os_ver) |os_ver| {
32653265 std.log.info("zig can provide libc for related target {s}-{s}.{d}-{s}", .{
......@@ -3530,16 +3530,16 @@ fn createModule(
35303530 }
35313531 }
35323532
3533 const target_query = parseTargetQueryOrReportFatalError(arena, target_parse_options);
3533 const target_query = std.zig.parseTargetQueryOrReportFatalError(arena, target_parse_options);
35343534 const adjusted_target_query = a: {
35353535 if (!target_query.isNative()) break :a target_query;
35363536 if (create_module.host_triple) |triple| target_parse_options.arch_os_abi = triple;
35373537 if (create_module.host_cpu) |cpu| target_parse_options.cpu_features = cpu;
35383538 if (create_module.host_dynamic_linker) |dl| target_parse_options.dynamic_linker = dl;
3539 break :a parseTargetQueryOrReportFatalError(arena, target_parse_options);
3539 break :a std.zig.parseTargetQueryOrReportFatalError(arena, target_parse_options);
35403540 };
35413541
3542 const target = resolveTargetQueryOrFatal(adjusted_target_query);
3542 const target = std.zig.resolveTargetQueryOrFatal(adjusted_target_query);
35433543 break :t .{
35443544 .result = target,
35453545 .is_native_os = target_query.isNativeOs(),
......@@ -4210,59 +4210,6 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void {
42104210 }
42114211}
42124212
4213fn parseTargetQueryOrReportFatalError(
4214 allocator: Allocator,
4215 opts: std.Target.Query.ParseOptions,
4216) std.Target.Query {
4217 var opts_with_diags = opts;
4218 var diags: std.Target.Query.ParseOptions.Diagnostics = .{};
4219 if (opts_with_diags.diagnostics == null) {
4220 opts_with_diags.diagnostics = &diags;
4221 }
4222 return std.Target.Query.parse(opts_with_diags) catch |err| switch (err) {
4223 error.UnknownCpuModel => {
4224 help: {
4225 var help_text = std.ArrayList(u8).init(allocator);
4226 defer help_text.deinit();
4227 for (diags.arch.?.allCpuModels()) |cpu| {
4228 help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help;
4229 }
4230 std.log.info("available CPUs for architecture '{s}':\n{s}", .{
4231 @tagName(diags.arch.?), help_text.items,
4232 });
4233 }
4234 fatal("unknown CPU: '{s}'", .{diags.cpu_name.?});
4235 },
4236 error.UnknownCpuFeature => {
4237 help: {
4238 var help_text = std.ArrayList(u8).init(allocator);
4239 defer help_text.deinit();
4240 for (diags.arch.?.allFeaturesList()) |feature| {
4241 help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help;
4242 }
4243 std.log.info("available CPU features for architecture '{s}':\n{s}", .{
4244 @tagName(diags.arch.?), help_text.items,
4245 });
4246 }
4247 fatal("unknown CPU feature: '{s}'", .{diags.unknown_feature_name.?});
4248 },
4249 error.UnknownObjectFormat => {
4250 help: {
4251 var help_text = std.ArrayList(u8).init(allocator);
4252 defer help_text.deinit();
4253 inline for (@typeInfo(std.Target.ObjectFormat).Enum.fields) |field| {
4254 help_text.writer().print(" {s}\n", .{field.name}) catch break :help;
4255 }
4256 std.log.info("available object formats:\n{s}", .{help_text.items});
4257 }
4258 fatal("unknown object format: '{s}'", .{opts.object_format.?});
4259 },
4260 else => |e| fatal("unable to parse target query '{s}': {s}", .{
4261 opts.arch_os_abi, @errorName(e),
4262 }),
4263 };
4264}
4265
42664213fn runOrTest(
42674214 comp: *Compilation,
42684215 gpa: Allocator,
......@@ -4871,9 +4818,9 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes:
48714818 .os_tag = .windows,
48724819 .abi = .msvc,
48734820 };
4874 const target = resolveTargetQueryOrFatal(target_query);
4821 const target = std.zig.resolveTargetQueryOrFatal(target_query);
48754822 const is_native_abi = target_query.isNativeAbi();
4876 const detected_libc = Compilation.detectLibCIncludeDirs(arena, zig_lib_dir, target, is_native_abi, true, null) catch |err| {
4823 const detected_libc = std.zig.LibCDirs.detect(arena, zig_lib_dir, target, is_native_abi, true, null) catch |err| {
48774824 if (cur_includes == .any) {
48784825 // fall back to mingw
48794826 cur_includes = .gnu;
......@@ -4899,9 +4846,9 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes:
48994846 .os_tag = .windows,
49004847 .abi = .gnu,
49014848 };
4902 const target = resolveTargetQueryOrFatal(target_query);
4849 const target = std.zig.resolveTargetQueryOrFatal(target_query);
49034850 const is_native_abi = target_query.isNativeAbi();
4904 const detected_libc = try Compilation.detectLibCIncludeDirs(arena, zig_lib_dir, target, is_native_abi, true, null);
4851 const detected_libc = try std.zig.LibCDirs.detect(arena, zig_lib_dir, target, is_native_abi, true, null);
49054852 return .{
49064853 .include_paths = detected_libc.libc_include_dir_list,
49074854 .target_abi = "gnu",
......@@ -4912,136 +4859,6 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes:
49124859 }
49134860}
49144861
4915const usage_libc =
4916 \\Usage: zig libc
4917 \\
4918 \\ Detect the native libc installation and print the resulting
4919 \\ paths to stdout. You can save this into a file and then edit
4920 \\ the paths to create a cross compilation libc kit. Then you
4921 \\ can pass `--libc [file]` for Zig to use it.
4922 \\
4923 \\Usage: zig libc [paths_file]
4924 \\
4925 \\ Parse a libc installation text file and validate it.
4926 \\
4927 \\Options:
4928 \\ -h, --help Print this help and exit
4929 \\ -target [name] <arch><sub>-<os>-<abi> see the targets command
4930 \\ -includes Print the libc include directories for the target
4931 \\
4932;
4933
4934fn cmdLibC(gpa: Allocator, args: []const []const u8) !void {
4935 var input_file: ?[]const u8 = null;
4936 var target_arch_os_abi: []const u8 = "native";
4937 var print_includes: bool = false;
4938 {
4939 var i: usize = 0;
4940 while (i < args.len) : (i += 1) {
4941 const arg = args[i];
4942 if (mem.startsWith(u8, arg, "-")) {
4943 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
4944 const stdout = io.getStdOut().writer();
4945 try stdout.writeAll(usage_libc);
4946 return cleanExit();
4947 } else if (mem.eql(u8, arg, "-target")) {
4948 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
4949 i += 1;
4950 target_arch_os_abi = args[i];
4951 } else if (mem.eql(u8, arg, "-includes")) {
4952 print_includes = true;
4953 } else {
4954 fatal("unrecognized parameter: '{s}'", .{arg});
4955 }
4956 } else if (input_file != null) {
4957 fatal("unexpected extra parameter: '{s}'", .{arg});
4958 } else {
4959 input_file = arg;
4960 }
4961 }
4962 }
4963
4964 const target_query = parseTargetQueryOrReportFatalError(gpa, .{
4965 .arch_os_abi = target_arch_os_abi,
4966 });
4967 const target = resolveTargetQueryOrFatal(target_query);
4968
4969 if (print_includes) {
4970 var arena_state = std.heap.ArenaAllocator.init(gpa);
4971 defer arena_state.deinit();
4972 const arena = arena_state.allocator();
4973
4974 const libc_installation: ?*LibCInstallation = libc: {
4975 if (input_file) |libc_file| {
4976 const libc = try arena.create(LibCInstallation);
4977 libc.* = LibCInstallation.parse(arena, libc_file, target) catch |err| {
4978 fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) });
4979 };
4980 break :libc libc;
4981 } else {
4982 break :libc null;
4983 }
4984 };
4985
4986 const self_exe_path = try introspect.findZigExePath(arena);
4987 var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
4988 fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)});
4989 };
4990 defer zig_lib_directory.handle.close();
4991
4992 const is_native_abi = target_query.isNativeAbi();
4993
4994 const libc_dirs = Compilation.detectLibCIncludeDirs(
4995 arena,
4996 zig_lib_directory.path.?,
4997 target,
4998 is_native_abi,
4999 true,
5000 libc_installation,
5001 ) catch |err| {
5002 const zig_target = try target.zigTriple(arena);
5003 fatal("unable to detect libc for target {s}: {s}", .{ zig_target, @errorName(err) });
5004 };
5005
5006 if (libc_dirs.libc_include_dir_list.len == 0) {
5007 const zig_target = try target.zigTriple(arena);
5008 fatal("no include dirs detected for target {s}", .{zig_target});
5009 }
5010
5011 var bw = io.bufferedWriter(io.getStdOut().writer());
5012 var writer = bw.writer();
5013 for (libc_dirs.libc_include_dir_list) |include_dir| {
5014 try writer.writeAll(include_dir);
5015 try writer.writeByte('\n');
5016 }
5017 try bw.flush();
5018 return cleanExit();
5019 }
5020
5021 if (input_file) |libc_file| {
5022 var libc = LibCInstallation.parse(gpa, libc_file, target) catch |err| {
5023 fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) });
5024 };
5025 defer libc.deinit(gpa);
5026 } else {
5027 if (!target_query.isNative()) {
5028 fatal("unable to detect libc for non-native target", .{});
5029 }
5030 var libc = LibCInstallation.findNative(.{
5031 .allocator = gpa,
5032 .verbose = true,
5033 .target = target,
5034 }) catch |err| {
5035 fatal("unable to detect native libc: {s}", .{@errorName(err)});
5036 };
5037 defer libc.deinit(gpa);
5038
5039 var bw = io.bufferedWriter(io.getStdOut().writer());
5040 try libc.render(bw.writer());
5041 try bw.flush();
5042 }
5043}
5044
50454862const usage_init =
50464863 \\Usage: zig init
50474864 \\
......@@ -5293,7 +5110,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
52935110
52945111 const target_query: std.Target.Query = .{};
52955112 const resolved_target: Package.Module.ResolvedTarget = .{
5296 .result = resolveTargetQueryOrFatal(target_query),
5113 .result = std.zig.resolveTargetQueryOrFatal(target_query),
52975114 .is_native_os = true,
52985115 .is_native_abi = true,
52995116 };
......@@ -5711,12 +5528,13 @@ fn jitCmd(
57115528 args: []const []const u8,
57125529 cmd_name: []const u8,
57135530 root_src_path: []const u8,
5531 prepend_zig_lib_dir_path: bool,
57145532) !void {
57155533 const color: Color = .auto;
57165534
57175535 const target_query: std.Target.Query = .{};
57185536 const resolved_target: Package.Module.ResolvedTarget = .{
5719 .result = resolveTargetQueryOrFatal(target_query),
5537 .result = std.zig.resolveTargetQueryOrFatal(target_query),
57205538 .is_native_os = true,
57215539 .is_native_abi = true,
57225540 };
......@@ -5739,6 +5557,7 @@ fn jitCmd(
57395557 .Debug
57405558 else
57415559 .ReleaseFast;
5560 const strip = optimize_mode != .Debug;
57425561 const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
57435562 const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
57445563
......@@ -5766,7 +5585,7 @@ fn jitCmd(
57665585 defer thread_pool.deinit();
57675586
57685587 var child_argv: std.ArrayListUnmanaged([]const u8) = .{};
5769 try child_argv.ensureUnusedCapacity(arena, args.len + 1);
5588 try child_argv.ensureUnusedCapacity(arena, args.len + 2);
57705589
57715590 // We want to release all the locks before executing the child process, so we make a nice
57725591 // big block here to ensure the cleanup gets run when we extract out our argv.
......@@ -5781,6 +5600,7 @@ fn jitCmd(
57815600
57825601 const config = try Compilation.Config.resolve(.{
57835602 .output_mode = .Exe,
5603 .root_strip = strip,
57845604 .root_optimize_mode = optimize_mode,
57855605 .resolved_target = resolved_target,
57865606 .have_zcu = true,
......@@ -5796,6 +5616,7 @@ fn jitCmd(
57965616 .inherited = .{
57975617 .resolved_target = resolved_target,
57985618 .optimize_mode = optimize_mode,
5619 .strip = strip,
57995620 },
58005621 .global = config,
58015622 .parent = null,
......@@ -5829,6 +5650,9 @@ fn jitCmd(
58295650 child_argv.appendAssumeCapacity(exe_path);
58305651 }
58315652
5653 if (prepend_zig_lib_dir_path)
5654 child_argv.appendAssumeCapacity(zig_lib_directory.path.?);
5655
58325656 child_argv.appendSliceAssumeCapacity(args);
58335657
58345658 if (process.can_execv) {
......@@ -6703,7 +6527,7 @@ fn warnAboutForeignBinaries(
67036527 link_libc: bool,
67046528) !void {
67056529 const host_query: std.Target.Query = .{};
6706 const host_target = resolveTargetQueryOrFatal(host_query);
6530 const host_target = std.zig.resolveTargetQueryOrFatal(host_query);
67076531
67086532 switch (std.zig.system.getExternalExecutor(host_target, target, .{ .link_libc = link_libc })) {
67096533 .native => return,
......@@ -7559,11 +7383,6 @@ fn parseWasiExecModel(s: []const u8) std.builtin.WasiExecModel {
75597383 fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{s});
75607384}
75617385
7562fn resolveTargetQueryOrFatal(target_query: std.Target.Query) std.Target {
7563 return std.zig.system.resolveTargetQuery(target_query) catch |err|
7564 fatal("unable to resolve target: {s}", .{@errorName(err)});
7565}
7566
75677386fn parseStackSize(s: []const u8) u64 {
75687387 return std.fmt.parseUnsigned(u64, s, 0) catch |err|
75697388 fatal("unable to parse stack size '{s}': {s}", .{ s, @errorName(err) });
src/musl.zig+2-25
......@@ -4,6 +4,7 @@ const mem = std.mem;
44const path = std.fs.path;
55const assert = std.debug.assert;
66const Module = @import("Package/Module.zig");
7const archName = std.zig.target.muslArchName;
78
89const Compilation = @import("Compilation.zig");
910const build_options = @import("build_options");
......@@ -294,30 +295,6 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr
294295 }
295296}
296297
297fn archName(arch: std.Target.Cpu.Arch) [:0]const u8 {
298 switch (arch) {
299 .aarch64, .aarch64_be => return "aarch64",
300 .arm, .armeb, .thumb, .thumbeb => return "arm",
301 .x86 => return "i386",
302 .mips, .mipsel => return "mips",
303 .mips64el, .mips64 => return "mips64",
304 .powerpc => return "powerpc",
305 .powerpc64, .powerpc64le => return "powerpc64",
306 .riscv64 => return "riscv64",
307 .s390x => return "s390x",
308 .wasm32, .wasm64 => return "wasm",
309 .x86_64 => return "x86_64",
310 else => unreachable,
311 }
312}
313
314pub fn archNameHeaders(arch: std.Target.Cpu.Arch) [:0]const u8 {
315 return switch (arch) {
316 .x86 => return "x86",
317 else => archName(arch),
318 };
319}
320
321298// Return true if musl has arch-specific crti/crtn sources.
322299// See lib/libc/musl/crt/ARCH/crt?.s .
323300pub fn needsCrtiCrtn(target: std.Target) bool {
......@@ -405,7 +382,7 @@ fn addCcArgs(
405382 const arch_name = archName(target.cpu.arch);
406383 const os_name = @tagName(target.os.tag);
407384 const triple = try std.fmt.allocPrint(arena, "{s}-{s}-musl", .{
408 archNameHeaders(target.cpu.arch), os_name,
385 std.zig.target.muslArchNameHeaders(target.cpu.arch), os_name,
409386 });
410387 const o_arg = if (want_O3) "-O3" else "-Os";
411388
src/print_env.zig+2-2
......@@ -47,9 +47,9 @@ pub fn cmdEnv(arena: Allocator, args: []const []const u8, stdout: std.fs.File.Wr
4747
4848 try jws.objectField("env");
4949 try jws.beginObject();
50 inline for (@typeInfo(introspect.EnvVar).Enum.fields) |field| {
50 inline for (@typeInfo(std.zig.EnvVar).Enum.fields) |field| {
5151 try jws.objectField(field.name);
52 try jws.write(try @field(introspect.EnvVar, field.name).get(arena));
52 try jws.write(try @field(std.zig.EnvVar, field.name).get(arena));
5353 }
5454 try jws.endObject();
5555
src/print_targets.zig+1-1
......@@ -67,7 +67,7 @@ pub fn cmdTargets(
6767
6868 try jws.objectField("libc");
6969 try jws.beginArray();
70 for (target.available_libcs) |libc| {
70 for (std.zig.target.available_libcs) |libc| {
7171 const tmp = try std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{
7272 @tagName(libc.arch), @tagName(libc.os), @tagName(libc.abi),
7373 });
src/target.zig-163
......@@ -6,169 +6,6 @@ const Feature = @import("Module.zig").Feature;
66
77pub const default_stack_protector_buffer_size = 4;
88
9pub const ArchOsAbi = struct {
10 arch: std.Target.Cpu.Arch,
11 os: std.Target.Os.Tag,
12 abi: std.Target.Abi,
13 os_ver: ?std.SemanticVersion = null,
14
15 // Minimum glibc version that provides support for the arch/os when ABI is GNU.
16 glibc_min: ?std.SemanticVersion = null,
17};
18
19pub const available_libcs = [_]ArchOsAbi{
20 .{ .arch = .aarch64_be, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 17, .patch = 0 } },
21 .{ .arch = .aarch64_be, .os = .linux, .abi = .musl },
22 .{ .arch = .aarch64_be, .os = .windows, .abi = .gnu },
23 .{ .arch = .aarch64, .os = .linux, .abi = .gnu },
24 .{ .arch = .aarch64, .os = .linux, .abi = .musl },
25 .{ .arch = .aarch64, .os = .windows, .abi = .gnu },
26 .{ .arch = .aarch64, .os = .macos, .abi = .none, .os_ver = .{ .major = 11, .minor = 0, .patch = 0 } },
27 .{ .arch = .armeb, .os = .linux, .abi = .gnueabi },
28 .{ .arch = .armeb, .os = .linux, .abi = .gnueabihf },
29 .{ .arch = .armeb, .os = .linux, .abi = .musleabi },
30 .{ .arch = .armeb, .os = .linux, .abi = .musleabihf },
31 .{ .arch = .armeb, .os = .windows, .abi = .gnu },
32 .{ .arch = .arm, .os = .linux, .abi = .gnueabi },
33 .{ .arch = .arm, .os = .linux, .abi = .gnueabihf },
34 .{ .arch = .arm, .os = .linux, .abi = .musleabi },
35 .{ .arch = .arm, .os = .linux, .abi = .musleabihf },
36 .{ .arch = .thumb, .os = .linux, .abi = .gnueabi },
37 .{ .arch = .thumb, .os = .linux, .abi = .gnueabihf },
38 .{ .arch = .thumb, .os = .linux, .abi = .musleabi },
39 .{ .arch = .thumb, .os = .linux, .abi = .musleabihf },
40 .{ .arch = .arm, .os = .windows, .abi = .gnu },
41 .{ .arch = .csky, .os = .linux, .abi = .gnueabi },
42 .{ .arch = .csky, .os = .linux, .abi = .gnueabihf },
43 .{ .arch = .x86, .os = .linux, .abi = .gnu },
44 .{ .arch = .x86, .os = .linux, .abi = .musl },
45 .{ .arch = .x86, .os = .windows, .abi = .gnu },
46 .{ .arch = .m68k, .os = .linux, .abi = .gnu },
47 .{ .arch = .m68k, .os = .linux, .abi = .musl },
48 .{ .arch = .mips64el, .os = .linux, .abi = .gnuabi64 },
49 .{ .arch = .mips64el, .os = .linux, .abi = .gnuabin32 },
50 .{ .arch = .mips64el, .os = .linux, .abi = .musl },
51 .{ .arch = .mips64, .os = .linux, .abi = .gnuabi64 },
52 .{ .arch = .mips64, .os = .linux, .abi = .gnuabin32 },
53 .{ .arch = .mips64, .os = .linux, .abi = .musl },
54 .{ .arch = .mipsel, .os = .linux, .abi = .gnueabi },
55 .{ .arch = .mipsel, .os = .linux, .abi = .gnueabihf },
56 .{ .arch = .mipsel, .os = .linux, .abi = .musl },
57 .{ .arch = .mips, .os = .linux, .abi = .gnueabi },
58 .{ .arch = .mips, .os = .linux, .abi = .gnueabihf },
59 .{ .arch = .mips, .os = .linux, .abi = .musl },
60 .{ .arch = .powerpc64le, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 19, .patch = 0 } },
61 .{ .arch = .powerpc64le, .os = .linux, .abi = .musl },
62 .{ .arch = .powerpc64, .os = .linux, .abi = .gnu },
63 .{ .arch = .powerpc64, .os = .linux, .abi = .musl },
64 .{ .arch = .powerpc, .os = .linux, .abi = .gnueabi },
65 .{ .arch = .powerpc, .os = .linux, .abi = .gnueabihf },
66 .{ .arch = .powerpc, .os = .linux, .abi = .musl },
67 .{ .arch = .riscv64, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 27, .patch = 0 } },
68 .{ .arch = .riscv64, .os = .linux, .abi = .musl },
69 .{ .arch = .s390x, .os = .linux, .abi = .gnu },
70 .{ .arch = .s390x, .os = .linux, .abi = .musl },
71 .{ .arch = .sparc, .os = .linux, .abi = .gnu },
72 .{ .arch = .sparc64, .os = .linux, .abi = .gnu },
73 .{ .arch = .wasm32, .os = .freestanding, .abi = .musl },
74 .{ .arch = .wasm32, .os = .wasi, .abi = .musl },
75 .{ .arch = .x86_64, .os = .linux, .abi = .gnu },
76 .{ .arch = .x86_64, .os = .linux, .abi = .gnux32 },
77 .{ .arch = .x86_64, .os = .linux, .abi = .musl },
78 .{ .arch = .x86_64, .os = .windows, .abi = .gnu },
79 .{ .arch = .x86_64, .os = .macos, .abi = .none, .os_ver = .{ .major = 10, .minor = 7, .patch = 0 } },
80};
81
82pub fn libCGenericName(target: std.Target) [:0]const u8 {
83 switch (target.os.tag) {
84 .windows => return "mingw",
85 .macos, .ios, .tvos, .watchos => return "darwin",
86 else => {},
87 }
88 switch (target.abi) {
89 .gnu,
90 .gnuabin32,
91 .gnuabi64,
92 .gnueabi,
93 .gnueabihf,
94 .gnuf32,
95 .gnuf64,
96 .gnusf,
97 .gnux32,
98 .gnuilp32,
99 => return "glibc",
100 .musl,
101 .musleabi,
102 .musleabihf,
103 .muslx32,
104 .none,
105 => return "musl",
106 .code16,
107 .eabi,
108 .eabihf,
109 .android,
110 .msvc,
111 .itanium,
112 .cygnus,
113 .coreclr,
114 .simulator,
115 .macabi,
116 => unreachable,
117
118 .pixel,
119 .vertex,
120 .geometry,
121 .hull,
122 .domain,
123 .compute,
124 .library,
125 .raygeneration,
126 .intersection,
127 .anyhit,
128 .closesthit,
129 .miss,
130 .callable,
131 .mesh,
132 .amplification,
133 => unreachable,
134 }
135}
136
137pub fn osArchName(target: std.Target) [:0]const u8 {
138 return switch (target.os.tag) {
139 .linux => switch (target.cpu.arch) {
140 .arm, .armeb, .thumb, .thumbeb => "arm",
141 .aarch64, .aarch64_be, .aarch64_32 => "aarch64",
142 .mips, .mipsel, .mips64, .mips64el => "mips",
143 .powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc",
144 .riscv32, .riscv64 => "riscv",
145 .sparc, .sparcel, .sparc64 => "sparc",
146 .x86, .x86_64 => "x86",
147 else => @tagName(target.cpu.arch),
148 },
149 else => @tagName(target.cpu.arch),
150 };
151}
152
153pub fn canBuildLibC(target: std.Target) bool {
154 for (available_libcs) |libc| {
155 if (target.cpu.arch == libc.arch and target.os.tag == libc.os and target.abi == libc.abi) {
156 if (target.os.tag == .macos) {
157 const ver = target.os.version_range.semver;
158 return ver.min.order(libc.os_ver.?) != .lt;
159 }
160 // Ensure glibc (aka *-linux-gnu) version is supported
161 if (target.isGnuLibC()) {
162 const min_glibc_ver = libc.glibc_min orelse return true;
163 const target_glibc_ver = target.os.version_range.linux.glibc;
164 return target_glibc_ver.order(min_glibc_ver) != .lt;
165 }
166 return true;
167 }
168 }
169 return false;
170}
171
1729pub fn cannotDynamicLink(target: std.Target) bool {
17310 return switch (target.os.tag) {
17411 .freestanding, .other => true,
src/wasi_libc.zig+1-3
......@@ -5,8 +5,6 @@ const path = std.fs.path;
55const Allocator = std.mem.Allocator;
66const Compilation = @import("Compilation.zig");
77const build_options = @import("build_options");
8const target_util = @import("target.zig");
9const musl = @import("musl.zig");
108
119pub const CRTFile = enum {
1210 crt1_reactor_o,
......@@ -273,7 +271,7 @@ fn addCCArgs(
273271 options: CCOptions,
274272) error{OutOfMemory}!void {
275273 const target = comp.getTarget();
276 const arch_name = musl.archNameHeaders(target.cpu.arch);
274 const arch_name = std.zig.target.muslArchNameHeaders(target.cpu.arch);
277275 const os_name = @tagName(target.os.tag);
278276 const triple = try std.fmt.allocPrint(arena, "{s}-{s}-musl", .{ arch_name, os_name });
279277 const o_arg = if (options.want_O3) "-O3" else "-Os";
src/windows_sdk.zig deleted-965
......@@ -1,965 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3
4const windows = std.os.windows;
5const RRF = windows.advapi32.RRF;
6
7const WINDOWS_KIT_REG_KEY = "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots";
8
9// https://learn.microsoft.com/en-us/windows/win32/msi/productversion
10const version_major_minor_max_length = "255.255".len;
11// note(bratishkaerik): i think ProductVersion in registry (created by Visual Studio installer) also follows this rule
12const product_version_max_length = version_major_minor_max_length + ".65535".len;
13
14/// Iterates via `iterator` and collects all folders with names starting with `optional_prefix`
15/// and similar to SemVer. Returns slice of folder names sorted in descending order.
16/// Caller owns result.
17fn iterateAndFilterBySemVer(
18 iterator: *std.fs.Dir.Iterator,
19 allocator: std.mem.Allocator,
20 comptime optional_prefix: ?[]const u8,
21) error{ OutOfMemory, VersionNotFound }![][]const u8 {
22 var dirs_filtered_list = std.ArrayList([]const u8).init(allocator);
23 errdefer {
24 for (dirs_filtered_list.items) |filtered_dir| allocator.free(filtered_dir);
25 dirs_filtered_list.deinit();
26 }
27
28 var normalized_name_buf: [std.fs.MAX_NAME_BYTES + ".0+build.0".len]u8 = undefined;
29 var normalized_name_fbs = std.io.fixedBufferStream(&normalized_name_buf);
30 const normalized_name_w = normalized_name_fbs.writer();
31 iterate_folder: while (true) : (normalized_name_fbs.reset()) {
32 const maybe_entry = iterator.next() catch continue :iterate_folder;
33 const entry = maybe_entry orelse break :iterate_folder;
34
35 if (entry.kind != .directory)
36 continue :iterate_folder;
37
38 // invalidated on next iteration
39 const subfolder_name = blk: {
40 if (comptime optional_prefix) |prefix| {
41 if (!std.mem.startsWith(u8, entry.name, prefix)) continue :iterate_folder;
42 break :blk entry.name[prefix.len..];
43 } else break :blk entry.name;
44 };
45
46 { // check if subfolder name looks similar to SemVer
47 switch (std.mem.count(u8, subfolder_name, ".")) {
48 0 => normalized_name_w.print("{s}.0.0+build.0", .{subfolder_name}) catch unreachable, // 17 => 17.0.0+build.0
49 1 => if (std.mem.indexOfScalar(u8, subfolder_name, '_')) |underscore_pos| blk: { // 17.0_9e9cbb98 => 17.0.1+build.9e9cbb98
50 var subfolder_name_tmp_copy_buf: [std.fs.MAX_NAME_BYTES]u8 = undefined;
51 const subfolder_name_tmp_copy = subfolder_name_tmp_copy_buf[0..subfolder_name.len];
52 @memcpy(subfolder_name_tmp_copy, subfolder_name);
53
54 subfolder_name_tmp_copy[underscore_pos] = '.'; // 17.0_9e9cbb98 => 17.0.9e9cbb98
55 var subfolder_name_parts = std.mem.splitScalar(u8, subfolder_name_tmp_copy, '.'); // [ 17, 0, 9e9cbb98 ]
56
57 const first = subfolder_name_parts.first(); // 17
58 const second = subfolder_name_parts.next().?; // 0
59 const third = subfolder_name_parts.rest(); // 9e9cbb98
60
61 break :blk normalized_name_w.print("{s}.{s}.1+build.{s}", .{ first, second, third }) catch unreachable; // [ 17, 0, 9e9cbb98 ] => 17.0.1+build.9e9cbb98
62 } else normalized_name_w.print("{s}.0+build.0", .{subfolder_name}) catch unreachable, // 17.0 => 17.0.0+build.0
63 else => normalized_name_w.print("{s}+build.0", .{subfolder_name}) catch unreachable, // 17.0.0 => 17.0.0+build.0
64 }
65 const subfolder_name_normalized: []const u8 = normalized_name_fbs.getWritten();
66 const sem_ver = std.SemanticVersion.parse(subfolder_name_normalized);
67 _ = sem_ver catch continue :iterate_folder;
68 }
69 // entry.name passed check
70
71 const subfolder_name_allocated = try allocator.dupe(u8, subfolder_name);
72 errdefer allocator.free(subfolder_name_allocated);
73 try dirs_filtered_list.append(subfolder_name_allocated);
74 }
75
76 const dirs_filtered_slice = try dirs_filtered_list.toOwnedSlice();
77 // Keep in mind that order of these names is not guaranteed by Windows,
78 // so we cannot just reverse or "while (popOrNull())" this ArrayList.
79 std.mem.sortUnstable([]const u8, dirs_filtered_slice, {}, struct {
80 fn desc(_: void, lhs: []const u8, rhs: []const u8) bool {
81 return std.mem.order(u8, lhs, rhs) == .gt;
82 }
83 }.desc);
84 return dirs_filtered_slice;
85}
86
87const RegistryWtf8 = struct {
88 key: windows.HKEY,
89
90 /// Assert that `key` is valid WTF-8 string
91 pub fn openKey(hkey: windows.HKEY, key: []const u8) error{KeyNotFound}!RegistryWtf8 {
92 const key_wtf16le: [:0]const u16 = key_wtf16le: {
93 var key_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined;
94 const key_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(key_wtf16le_buf[0..], key) catch |err| switch (err) {
95 error.InvalidWtf8 => unreachable,
96 };
97 key_wtf16le_buf[key_wtf16le_len] = 0;
98 break :key_wtf16le key_wtf16le_buf[0..key_wtf16le_len :0];
99 };
100
101 const registry_wtf16le = try RegistryWtf16Le.openKey(hkey, key_wtf16le);
102 return RegistryWtf8{ .key = registry_wtf16le.key };
103 }
104
105 /// Closes key, after that usage is invalid
106 pub fn closeKey(self: *const RegistryWtf8) void {
107 const return_code_int: windows.HRESULT = windows.advapi32.RegCloseKey(self.key);
108 const return_code: windows.Win32Error = @enumFromInt(return_code_int);
109 switch (return_code) {
110 .SUCCESS => {},
111 else => {},
112 }
113 }
114
115 /// Get string from registry.
116 /// Caller owns result.
117 pub fn getString(self: *const RegistryWtf8, allocator: std.mem.Allocator, subkey: []const u8, value_name: []const u8) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]u8 {
118 const subkey_wtf16le: [:0]const u16 = subkey_wtf16le: {
119 var subkey_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined;
120 const subkey_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(subkey_wtf16le_buf[0..], subkey) catch unreachable;
121 subkey_wtf16le_buf[subkey_wtf16le_len] = 0;
122 break :subkey_wtf16le subkey_wtf16le_buf[0..subkey_wtf16le_len :0];
123 };
124
125 const value_name_wtf16le: [:0]const u16 = value_name_wtf16le: {
126 var value_name_wtf16le_buf: [RegistryWtf16Le.value_name_max_len]u16 = undefined;
127 const value_name_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(value_name_wtf16le_buf[0..], value_name) catch unreachable;
128 value_name_wtf16le_buf[value_name_wtf16le_len] = 0;
129 break :value_name_wtf16le value_name_wtf16le_buf[0..value_name_wtf16le_len :0];
130 };
131
132 const registry_wtf16le = RegistryWtf16Le{ .key = self.key };
133 const value_wtf16le = try registry_wtf16le.getString(allocator, subkey_wtf16le, value_name_wtf16le);
134 defer allocator.free(value_wtf16le);
135
136 const value_wtf8: []u8 = try std.unicode.wtf16LeToWtf8Alloc(allocator, value_wtf16le);
137 errdefer allocator.free(value_wtf8);
138
139 return value_wtf8;
140 }
141
142 /// Get DWORD (u32) from registry.
143 pub fn getDword(self: *const RegistryWtf8, subkey: []const u8, value_name: []const u8) error{ ValueNameNotFound, NotADword, DwordTooLong, DwordNotFound }!u32 {
144 const subkey_wtf16le: [:0]const u16 = subkey_wtf16le: {
145 var subkey_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined;
146 const subkey_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(subkey_wtf16le_buf[0..], subkey) catch unreachable;
147 subkey_wtf16le_buf[subkey_wtf16le_len] = 0;
148 break :subkey_wtf16le subkey_wtf16le_buf[0..subkey_wtf16le_len :0];
149 };
150
151 const value_name_wtf16le: [:0]const u16 = value_name_wtf16le: {
152 var value_name_wtf16le_buf: [RegistryWtf16Le.value_name_max_len]u16 = undefined;
153 const value_name_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(value_name_wtf16le_buf[0..], value_name) catch unreachable;
154 value_name_wtf16le_buf[value_name_wtf16le_len] = 0;
155 break :value_name_wtf16le value_name_wtf16le_buf[0..value_name_wtf16le_len :0];
156 };
157
158 const registry_wtf16le = RegistryWtf16Le{ .key = self.key };
159 return try registry_wtf16le.getDword(subkey_wtf16le, value_name_wtf16le);
160 }
161
162 /// Under private space with flags:
163 /// KEY_QUERY_VALUE and KEY_ENUMERATE_SUB_KEYS.
164 /// After finishing work, call `closeKey`.
165 pub fn loadFromPath(absolute_path: []const u8) error{KeyNotFound}!RegistryWtf8 {
166 const absolute_path_wtf16le: [:0]const u16 = absolute_path_wtf16le: {
167 var absolute_path_wtf16le_buf: [RegistryWtf16Le.value_name_max_len]u16 = undefined;
168 const absolute_path_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(absolute_path_wtf16le_buf[0..], absolute_path) catch unreachable;
169 absolute_path_wtf16le_buf[absolute_path_wtf16le_len] = 0;
170 break :absolute_path_wtf16le absolute_path_wtf16le_buf[0..absolute_path_wtf16le_len :0];
171 };
172
173 const registry_wtf16le = try RegistryWtf16Le.loadFromPath(absolute_path_wtf16le);
174 return RegistryWtf8{ .key = registry_wtf16le.key };
175 }
176};
177
178const RegistryWtf16Le = struct {
179 key: windows.HKEY,
180
181 /// Includes root key (f.e. HKEY_LOCAL_MACHINE).
182 /// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
183 pub const key_name_max_len = 255;
184 /// In Unicode characters.
185 /// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
186 pub const value_name_max_len = 16_383;
187
188 /// Under HKEY_LOCAL_MACHINE with flags:
189 /// KEY_QUERY_VALUE, KEY_WOW64_32KEY, and KEY_ENUMERATE_SUB_KEYS.
190 /// After finishing work, call `closeKey`.
191 fn openKey(hkey: windows.HKEY, key_wtf16le: [:0]const u16) error{KeyNotFound}!RegistryWtf16Le {
192 var key: windows.HKEY = undefined;
193 const return_code_int: windows.HRESULT = windows.advapi32.RegOpenKeyExW(
194 hkey,
195 key_wtf16le,
196 0,
197 windows.KEY_QUERY_VALUE | windows.KEY_WOW64_32KEY | windows.KEY_ENUMERATE_SUB_KEYS,
198 &key,
199 );
200 const return_code: windows.Win32Error = @enumFromInt(return_code_int);
201 switch (return_code) {
202 .SUCCESS => {},
203 .FILE_NOT_FOUND => return error.KeyNotFound,
204
205 else => return error.KeyNotFound,
206 }
207 return RegistryWtf16Le{ .key = key };
208 }
209
210 /// Closes key, after that usage is invalid
211 fn closeKey(self: *const RegistryWtf16Le) void {
212 const return_code_int: windows.HRESULT = windows.advapi32.RegCloseKey(self.key);
213 const return_code: windows.Win32Error = @enumFromInt(return_code_int);
214 switch (return_code) {
215 .SUCCESS => {},
216 else => {},
217 }
218 }
219
220 /// Get string ([:0]const u16) from registry.
221 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 {
222 var actual_type: windows.ULONG = undefined;
223
224 // Calculating length to allocate
225 var value_wtf16le_buf_size: u32 = 0; // in bytes, including any terminating NUL character or characters.
226 var return_code_int: windows.HRESULT = windows.advapi32.RegGetValueW(
227 self.key,
228 subkey_wtf16le,
229 value_name_wtf16le,
230 RRF.RT_REG_SZ,
231 &actual_type,
232 null,
233 &value_wtf16le_buf_size,
234 );
235
236 // Check returned code and type
237 var return_code: windows.Win32Error = @enumFromInt(return_code_int);
238 switch (return_code) {
239 .SUCCESS => std.debug.assert(value_wtf16le_buf_size != 0),
240 .MORE_DATA => unreachable, // We are only reading length
241 .FILE_NOT_FOUND => return error.ValueNameNotFound,
242 .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY
243 else => return error.StringNotFound,
244 }
245 switch (actual_type) {
246 windows.REG.SZ => {},
247 else => return error.NotAString,
248 }
249
250 const value_wtf16le_buf: []u16 = try allocator.alloc(u16, std.math.divCeil(u32, value_wtf16le_buf_size, 2) catch unreachable);
251 errdefer allocator.free(value_wtf16le_buf);
252
253 return_code_int = windows.advapi32.RegGetValueW(
254 self.key,
255 subkey_wtf16le,
256 value_name_wtf16le,
257 RRF.RT_REG_SZ,
258 &actual_type,
259 value_wtf16le_buf.ptr,
260 &value_wtf16le_buf_size,
261 );
262
263 // Check returned code and (just in case) type again.
264 return_code = @enumFromInt(return_code_int);
265 switch (return_code) {
266 .SUCCESS => {},
267 .MORE_DATA => unreachable, // Calculated first time length should be enough, even overestimated
268 .FILE_NOT_FOUND => return error.ValueNameNotFound,
269 .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY
270 else => return error.StringNotFound,
271 }
272 switch (actual_type) {
273 windows.REG.SZ => {},
274 else => return error.NotAString,
275 }
276
277 const value_wtf16le: []const u16 = value_wtf16le: {
278 // note(bratishkaerik): somehow returned value in `buf_len` is overestimated by Windows and contains extra space
279 // we will just search for zero termination and forget length
280 // Windows sure is strange
281 const value_wtf16le_overestimated: [*:0]const u16 = @ptrCast(value_wtf16le_buf.ptr);
282 break :value_wtf16le std.mem.span(value_wtf16le_overestimated);
283 };
284
285 _ = allocator.resize(value_wtf16le_buf, value_wtf16le.len);
286 return value_wtf16le;
287 }
288
289 /// Get DWORD (u32) from registry.
290 fn getDword(self: *const RegistryWtf16Le, subkey_wtf16le: [:0]const u16, value_name_wtf16le: [:0]const u16) error{ ValueNameNotFound, NotADword, DwordTooLong, DwordNotFound }!u32 {
291 var actual_type: windows.ULONG = undefined;
292 var reg_size: u32 = @sizeOf(u32);
293 var reg_value: u32 = 0;
294
295 const return_code_int: windows.HRESULT = windows.advapi32.RegGetValueW(
296 self.key,
297 subkey_wtf16le,
298 value_name_wtf16le,
299 RRF.RT_REG_DWORD,
300 &actual_type,
301 &reg_value,
302 &reg_size,
303 );
304 const return_code: windows.Win32Error = @enumFromInt(return_code_int);
305 switch (return_code) {
306 .SUCCESS => {},
307 .MORE_DATA => return error.DwordTooLong,
308 .FILE_NOT_FOUND => return error.ValueNameNotFound,
309 .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY
310 else => return error.DwordNotFound,
311 }
312
313 switch (actual_type) {
314 windows.REG.DWORD => {},
315 else => return error.NotADword,
316 }
317
318 return reg_value;
319 }
320
321 /// Under private space with flags:
322 /// KEY_QUERY_VALUE and KEY_ENUMERATE_SUB_KEYS.
323 /// After finishing work, call `closeKey`.
324 fn loadFromPath(absolute_path_as_wtf16le: [:0]const u16) error{KeyNotFound}!RegistryWtf16Le {
325 var key: windows.HKEY = undefined;
326
327 const return_code_int: windows.HRESULT = std.os.windows.advapi32.RegLoadAppKeyW(
328 absolute_path_as_wtf16le,
329 &key,
330 windows.KEY_QUERY_VALUE | windows.KEY_ENUMERATE_SUB_KEYS,
331 0,
332 0,
333 );
334 const return_code: windows.Win32Error = @enumFromInt(return_code_int);
335 switch (return_code) {
336 .SUCCESS => {},
337 else => return error.KeyNotFound,
338 }
339
340 return RegistryWtf16Le{ .key = key };
341 }
342};
343
344pub const Windows10Sdk = struct {
345 path: []const u8,
346 version: []const u8,
347
348 /// Find path and version of Windows 10 SDK.
349 /// Caller owns the result's fields.
350 /// After finishing work, call `free(allocator)`.
351 fn find(allocator: std.mem.Allocator) error{ OutOfMemory, Windows10SdkNotFound, PathTooLong, VersionTooLong }!Windows10Sdk {
352 const v10_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0") catch |err| switch (err) {
353 error.KeyNotFound => return error.Windows10SdkNotFound,
354 };
355 defer v10_key.closeKey();
356
357 const path: []const u8 = path10: {
358 const path_maybe_with_trailing_slash = v10_key.getString(allocator, "", "InstallationFolder") catch |err| switch (err) {
359 error.NotAString => return error.Windows10SdkNotFound,
360 error.ValueNameNotFound => return error.Windows10SdkNotFound,
361 error.StringNotFound => return error.Windows10SdkNotFound,
362
363 error.OutOfMemory => return error.OutOfMemory,
364 };
365
366 if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
367 allocator.free(path_maybe_with_trailing_slash);
368 return error.PathTooLong;
369 }
370
371 var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
372 errdefer path.deinit();
373
374 // String might contain trailing slash, so trim it here
375 if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
376
377 const path_without_trailing_slash = try path.toOwnedSlice();
378 break :path10 path_without_trailing_slash;
379 };
380 errdefer allocator.free(path);
381
382 const version: []const u8 = version10: {
383
384 // note(dimenus): Microsoft doesn't include the .0 in the ProductVersion key....
385 const version_without_0 = v10_key.getString(allocator, "", "ProductVersion") catch |err| switch (err) {
386 error.NotAString => return error.Windows10SdkNotFound,
387 error.ValueNameNotFound => return error.Windows10SdkNotFound,
388 error.StringNotFound => return error.Windows10SdkNotFound,
389
390 error.OutOfMemory => return error.OutOfMemory,
391 };
392 if (version_without_0.len + ".0".len > product_version_max_length) {
393 allocator.free(version_without_0);
394 return error.VersionTooLong;
395 }
396
397 var version = std.ArrayList(u8).fromOwnedSlice(allocator, version_without_0);
398 errdefer version.deinit();
399
400 try version.appendSlice(".0");
401
402 const version_with_0 = try version.toOwnedSlice();
403 break :version10 version_with_0;
404 };
405 errdefer allocator.free(version);
406
407 return Windows10Sdk{ .path = path, .version = version };
408 }
409
410 /// Check whether this version is enumerated in registry.
411 fn isValidVersion(windows10sdk: *const Windows10Sdk) bool {
412 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
413 const reg_query_as_wtf8 = std.fmt.bufPrint(buf[0..], "{s}\\{s}\\Installed Options", .{ WINDOWS_KIT_REG_KEY, windows10sdk.version }) catch |err| switch (err) {
414 error.NoSpaceLeft => return false,
415 };
416
417 const options_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, reg_query_as_wtf8) catch |err| switch (err) {
418 error.KeyNotFound => return false,
419 };
420 defer options_key.closeKey();
421
422 const option_name = comptime switch (builtin.target.cpu.arch) {
423 .arm, .armeb => "OptionId.DesktopCPParm",
424 .aarch64 => "OptionId.DesktopCPParm64",
425 .x86_64 => "OptionId.DesktopCPPx64",
426 .x86 => "OptionId.DesktopCPPx86",
427 else => |tag| @compileError("Windows 10 SDK cannot be detected on architecture " ++ tag),
428 };
429
430 const reg_value = options_key.getDword("", option_name) catch return false;
431 return (reg_value == 1);
432 }
433
434 fn free(self: *const Windows10Sdk, allocator: std.mem.Allocator) void {
435 allocator.free(self.path);
436 allocator.free(self.version);
437 }
438};
439
440pub const Windows81Sdk = struct {
441 path: []const u8,
442 version: []const u8,
443
444 /// Find path and version of Windows 8.1 SDK.
445 /// Caller owns the result's fields.
446 /// After finishing work, call `free(allocator)`.
447 fn find(allocator: std.mem.Allocator, roots_key: *const RegistryWtf8) error{ OutOfMemory, Windows81SdkNotFound, PathTooLong, VersionTooLong }!Windows81Sdk {
448 const path: []const u8 = path81: {
449 const path_maybe_with_trailing_slash = roots_key.getString(allocator, "", "KitsRoot81") catch |err| switch (err) {
450 error.NotAString => return error.Windows81SdkNotFound,
451 error.ValueNameNotFound => return error.Windows81SdkNotFound,
452 error.StringNotFound => return error.Windows81SdkNotFound,
453
454 error.OutOfMemory => return error.OutOfMemory,
455 };
456 if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
457 allocator.free(path_maybe_with_trailing_slash);
458 return error.PathTooLong;
459 }
460
461 var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
462 errdefer path.deinit();
463
464 // String might contain trailing slash, so trim it here
465 if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
466
467 const path_without_trailing_slash = try path.toOwnedSlice();
468 break :path81 path_without_trailing_slash;
469 };
470 errdefer allocator.free(path);
471
472 const version: []const u8 = version81: {
473 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
474 const sdk_lib_dir_path = std.fmt.bufPrint(buf[0..], "{s}\\Lib\\", .{path}) catch |err| switch (err) {
475 error.NoSpaceLeft => return error.PathTooLong,
476 };
477 if (!std.fs.path.isAbsolute(sdk_lib_dir_path)) return error.Windows81SdkNotFound;
478
479 // enumerate files in sdk path looking for latest version
480 var sdk_lib_dir = std.fs.openDirAbsolute(sdk_lib_dir_path, .{
481 .iterate = true,
482 }) catch |err| switch (err) {
483 error.NameTooLong => return error.PathTooLong,
484 else => return error.Windows81SdkNotFound,
485 };
486 defer sdk_lib_dir.close();
487
488 var iterator = sdk_lib_dir.iterate();
489 const versions = iterateAndFilterBySemVer(&iterator, allocator, "winv") catch |err| switch (err) {
490 error.OutOfMemory => return error.OutOfMemory,
491 error.VersionNotFound => return error.Windows81SdkNotFound,
492 };
493 defer {
494 for (versions) |version| allocator.free(version);
495 allocator.free(versions);
496 }
497 const latest_version = try allocator.dupe(u8, versions[0]);
498 break :version81 latest_version;
499 };
500 errdefer allocator.free(version);
501
502 return Windows81Sdk{ .path = path, .version = version };
503 }
504
505 fn free(self: *const Windows81Sdk, allocator: std.mem.Allocator) void {
506 allocator.free(self.path);
507 allocator.free(self.version);
508 }
509};
510
511pub const ZigWindowsSDK = struct {
512 windows10sdk: ?Windows10Sdk,
513 windows81sdk: ?Windows81Sdk,
514 msvc_lib_dir: ?[]const u8,
515
516 /// Find path and version of Windows 10 SDK and Windows 8.1 SDK, and find path to MSVC's `lib/` directory.
517 /// Caller owns the result's fields.
518 /// After finishing work, call `free(allocator)`.
519 pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, NotFound, PathTooLong }!ZigWindowsSDK {
520 if (builtin.os.tag != .windows) return error.NotFound;
521
522 //note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed
523 const roots_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, WINDOWS_KIT_REG_KEY) catch |err| switch (err) {
524 error.KeyNotFound => return error.NotFound,
525 };
526 defer roots_key.closeKey();
527
528 const windows10sdk: ?Windows10Sdk = blk: {
529 const windows10sdk = Windows10Sdk.find(allocator) catch |err| switch (err) {
530 error.Windows10SdkNotFound,
531 error.PathTooLong,
532 error.VersionTooLong,
533 => break :blk null,
534 error.OutOfMemory => return error.OutOfMemory,
535 };
536 const is_valid_version = windows10sdk.isValidVersion();
537 if (!is_valid_version) break :blk null;
538 break :blk windows10sdk;
539 };
540 errdefer if (windows10sdk) |*w| w.free(allocator);
541
542 const windows81sdk: ?Windows81Sdk = blk: {
543 const windows81sdk = Windows81Sdk.find(allocator, &roots_key) catch |err| switch (err) {
544 error.Windows81SdkNotFound => break :blk null,
545 error.PathTooLong => break :blk null,
546 error.VersionTooLong => break :blk null,
547 error.OutOfMemory => return error.OutOfMemory,
548 };
549 // no check
550 break :blk windows81sdk;
551 };
552 errdefer if (windows81sdk) |*w| w.free(allocator);
553
554 const msvc_lib_dir: ?[]const u8 = MsvcLibDir.find(allocator) catch |err| switch (err) {
555 error.MsvcLibDirNotFound => null,
556 error.OutOfMemory => return error.OutOfMemory,
557 };
558 errdefer allocator.free(msvc_lib_dir);
559
560 return ZigWindowsSDK{
561 .windows10sdk = windows10sdk,
562 .windows81sdk = windows81sdk,
563 .msvc_lib_dir = msvc_lib_dir,
564 };
565 }
566
567 pub fn free(self: *const ZigWindowsSDK, allocator: std.mem.Allocator) void {
568 if (self.windows10sdk) |*w10sdk| {
569 w10sdk.free(allocator);
570 }
571 if (self.windows81sdk) |*w81sdk| {
572 w81sdk.free(allocator);
573 }
574 if (self.msvc_lib_dir) |msvc_lib_dir| {
575 allocator.free(msvc_lib_dir);
576 }
577 }
578};
579
580const MsvcLibDir = struct {
581 fn findInstancesDirViaCLSID(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {
582 const setup_configuration_clsid = "{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}";
583 const setup_config_key = RegistryWtf8.openKey(windows.HKEY_CLASSES_ROOT, "CLSID\\" ++ setup_configuration_clsid) catch |err| switch (err) {
584 error.KeyNotFound => return error.PathNotFound,
585 };
586 defer setup_config_key.closeKey();
587
588 const dll_path = setup_config_key.getString(allocator, "InprocServer32", "") catch |err| switch (err) {
589 error.NotAString,
590 error.ValueNameNotFound,
591 error.StringNotFound,
592 => return error.PathNotFound,
593
594 error.OutOfMemory => return error.OutOfMemory,
595 };
596 defer allocator.free(dll_path);
597
598 var path_it = std.fs.path.componentIterator(dll_path) catch return error.PathNotFound;
599 // the .dll filename
600 _ = path_it.last();
601 const root_path = while (path_it.previous()) |dir_component| {
602 if (std.ascii.eqlIgnoreCase(dir_component.name, "VisualStudio")) {
603 break dir_component.path;
604 }
605 } else {
606 return error.PathNotFound;
607 };
608
609 const instances_path = try std.fs.path.join(allocator, &.{ root_path, "Packages", "_Instances" });
610 defer allocator.free(instances_path);
611
612 return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return error.PathNotFound;
613 }
614
615 fn findInstancesDir(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {
616 // First try to get the path from the .dll that would have been
617 // loaded via COM for SetupConfiguration.
618 return findInstancesDirViaCLSID(allocator) catch |orig_err| {
619 // If that can't be found, fall back to manually appending
620 // `Microsoft\VisualStudio\Packages\_Instances` to %PROGRAMDATA%
621 const program_data = std.process.getEnvVarOwned(allocator, "PROGRAMDATA") catch |err| switch (err) {
622 error.OutOfMemory => |e| return e,
623 else => return orig_err,
624 };
625 defer allocator.free(program_data);
626
627 const instances_path = try std.fs.path.join(allocator, &.{ program_data, "Microsoft", "VisualStudio", "Packages", "_Instances" });
628 defer allocator.free(instances_path);
629
630 return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return orig_err;
631 };
632 }
633
634 /// Intended to be equivalent to `ISetupHelper.ParseVersion`
635 /// Example: 17.4.33205.214 -> 0x0011000481b500d6
636 fn parseVersionQuad(version: []const u8) error{InvalidVersion}!u64 {
637 var it = std.mem.splitScalar(u8, version, '.');
638 const a = it.next() orelse return error.InvalidVersion;
639 const b = it.next() orelse return error.InvalidVersion;
640 const c = it.next() orelse return error.InvalidVersion;
641 const d = it.next() orelse return error.InvalidVersion;
642 if (it.next()) |_| return error.InvalidVersion;
643 var result: u64 = undefined;
644 var result_bytes = std.mem.asBytes(&result);
645
646 std.mem.writeInt(
647 u16,
648 result_bytes[0..2],
649 std.fmt.parseUnsigned(u16, d, 10) catch return error.InvalidVersion,
650 .little,
651 );
652 std.mem.writeInt(
653 u16,
654 result_bytes[2..4],
655 std.fmt.parseUnsigned(u16, c, 10) catch return error.InvalidVersion,
656 .little,
657 );
658 std.mem.writeInt(
659 u16,
660 result_bytes[4..6],
661 std.fmt.parseUnsigned(u16, b, 10) catch return error.InvalidVersion,
662 .little,
663 );
664 std.mem.writeInt(
665 u16,
666 result_bytes[6..8],
667 std.fmt.parseUnsigned(u16, a, 10) catch return error.InvalidVersion,
668 .little,
669 );
670
671 return result;
672 }
673
674 /// Intended to be equivalent to ISetupConfiguration.EnumInstances:
675 /// https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.setup.configuration
676 /// but without the use of COM in order to avoid a dependency on ole32.dll
677 ///
678 /// The logic in this function is intended to match what ISetupConfiguration does
679 /// under-the-hood, as verified using Procmon.
680 fn findViaCOM(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
681 // Typically `%PROGRAMDATA%\Microsoft\VisualStudio\Packages\_Instances`
682 // This will contain directories with names of instance IDs like 80a758ca,
683 // which will contain `state.json` files that have the version and
684 // installation directory.
685 var instances_dir = try findInstancesDir(allocator);
686 defer instances_dir.close();
687
688 var state_subpath_buf: [std.fs.MAX_NAME_BYTES + 32]u8 = undefined;
689 var latest_version_lib_dir = std.ArrayListUnmanaged(u8){};
690 errdefer latest_version_lib_dir.deinit(allocator);
691
692 var latest_version: u64 = 0;
693 var instances_dir_it = instances_dir.iterateAssumeFirstIteration();
694 while (instances_dir_it.next() catch return error.PathNotFound) |entry| {
695 if (entry.kind != .directory) continue;
696
697 var fbs = std.io.fixedBufferStream(&state_subpath_buf);
698 const writer = fbs.writer();
699
700 writer.writeAll(entry.name) catch unreachable;
701 writer.writeByte(std.fs.path.sep) catch unreachable;
702 writer.writeAll("state.json") catch unreachable;
703
704 const json_contents = instances_dir.readFileAlloc(allocator, fbs.getWritten(), std.math.maxInt(usize)) catch continue;
705 defer allocator.free(json_contents);
706
707 var parsed = std.json.parseFromSlice(std.json.Value, allocator, json_contents, .{}) catch continue;
708 defer parsed.deinit();
709
710 if (parsed.value != .object) continue;
711 const catalog_info = parsed.value.object.get("catalogInfo") orelse continue;
712 if (catalog_info != .object) continue;
713 const product_version_value = catalog_info.object.get("buildVersion") orelse continue;
714 if (product_version_value != .string) continue;
715 const product_version_text = product_version_value.string;
716 const parsed_version = parseVersionQuad(product_version_text) catch continue;
717
718 // We want to end up with the most recent version installed
719 if (parsed_version <= latest_version) continue;
720
721 const installation_path = parsed.value.object.get("installationPath") orelse continue;
722 if (installation_path != .string) continue;
723
724 const lib_dir_path = libDirFromInstallationPath(allocator, installation_path.string) catch |err| switch (err) {
725 error.OutOfMemory => |e| return e,
726 error.PathNotFound => continue,
727 };
728 defer allocator.free(lib_dir_path);
729
730 latest_version_lib_dir.clearRetainingCapacity();
731 try latest_version_lib_dir.appendSlice(allocator, lib_dir_path);
732 latest_version = parsed_version;
733 }
734
735 if (latest_version_lib_dir.items.len == 0) return error.PathNotFound;
736 return latest_version_lib_dir.toOwnedSlice(allocator);
737 }
738
739 fn libDirFromInstallationPath(allocator: std.mem.Allocator, installation_path: []const u8) error{ OutOfMemory, PathNotFound }![]const u8 {
740 var lib_dir_buf = try std.ArrayList(u8).initCapacity(allocator, installation_path.len + 64);
741 errdefer lib_dir_buf.deinit();
742
743 lib_dir_buf.appendSliceAssumeCapacity(installation_path);
744
745 if (!std.fs.path.isSep(lib_dir_buf.getLast())) {
746 try lib_dir_buf.append('\\');
747 }
748 const installation_path_with_trailing_sep_len = lib_dir_buf.items.len;
749
750 try lib_dir_buf.appendSlice("VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
751 var default_tools_version_buf: [512]u8 = undefined;
752 const default_tools_version_contents = std.fs.cwd().readFile(lib_dir_buf.items, &default_tools_version_buf) catch {
753 return error.PathNotFound;
754 };
755 var tokenizer = std.mem.tokenizeAny(u8, default_tools_version_contents, " \r\n");
756 const default_tools_version = tokenizer.next() orelse return error.PathNotFound;
757
758 lib_dir_buf.shrinkRetainingCapacity(installation_path_with_trailing_sep_len);
759 try lib_dir_buf.appendSlice("VC\\Tools\\MSVC\\");
760 try lib_dir_buf.appendSlice(default_tools_version);
761 const folder_with_arch = "\\Lib\\" ++ comptime switch (builtin.target.cpu.arch) {
762 .x86 => "x86",
763 .x86_64 => "x64",
764 .arm, .armeb => "arm",
765 .aarch64 => "arm64",
766 else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag),
767 };
768 try lib_dir_buf.appendSlice(folder_with_arch);
769
770 if (!verifyLibDir(lib_dir_buf.items)) {
771 return error.PathNotFound;
772 }
773
774 return lib_dir_buf.toOwnedSlice();
775 }
776
777 // 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
778 fn findViaRegistry(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
779
780 // %localappdata%\Microsoft\VisualStudio\
781 // %appdata%\Local\Microsoft\VisualStudio\
782 const visualstudio_folder_path = std.fs.getAppDataDir(allocator, "Microsoft\\VisualStudio\\") catch return error.PathNotFound;
783 defer allocator.free(visualstudio_folder_path);
784
785 const vs_versions: []const []const u8 = vs_versions: {
786 if (!std.fs.path.isAbsolute(visualstudio_folder_path)) return error.PathNotFound;
787 // enumerate folders that contain `privateregistry.bin`, looking for all versions
788 // f.i. %localappdata%\Microsoft\VisualStudio\17.0_9e9cbb98\
789 var visualstudio_folder = std.fs.openDirAbsolute(visualstudio_folder_path, .{
790 .iterate = true,
791 }) catch return error.PathNotFound;
792 defer visualstudio_folder.close();
793
794 var iterator = visualstudio_folder.iterate();
795 const versions = iterateAndFilterBySemVer(&iterator, allocator, null) catch |err| switch (err) {
796 error.OutOfMemory => return error.OutOfMemory,
797 error.VersionNotFound => return error.PathNotFound,
798 };
799 break :vs_versions versions;
800 };
801 defer {
802 for (vs_versions) |vs_version| allocator.free(vs_version);
803 allocator.free(vs_versions);
804 }
805 var config_subkey_buf: [RegistryWtf16Le.key_name_max_len * 2]u8 = undefined;
806 const source_directories: []const u8 = source_directories: for (vs_versions) |vs_version| {
807 const privateregistry_absolute_path = std.fs.path.join(allocator, &.{ visualstudio_folder_path, vs_version, "privateregistry.bin" }) catch continue;
808 defer allocator.free(privateregistry_absolute_path);
809 if (!std.fs.path.isAbsolute(privateregistry_absolute_path)) continue;
810
811 const visualstudio_registry = RegistryWtf8.loadFromPath(privateregistry_absolute_path) catch continue;
812 defer visualstudio_registry.closeKey();
813
814 const config_subkey = std.fmt.bufPrint(config_subkey_buf[0..], "Software\\Microsoft\\VisualStudio\\{s}_Config", .{vs_version}) catch unreachable;
815
816 const source_directories_value = visualstudio_registry.getString(allocator, config_subkey, "Source Directories") catch |err| switch (err) {
817 error.OutOfMemory => return error.OutOfMemory,
818 else => continue,
819 };
820 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
821 allocator.free(source_directories_value);
822 continue;
823 }
824
825 break :source_directories source_directories_value;
826 } else return error.PathNotFound;
827 defer allocator.free(source_directories);
828
829 var source_directories_splitted = std.mem.splitScalar(u8, source_directories, ';');
830
831 const msvc_dir: []const u8 = msvc_dir: {
832 const msvc_include_dir_maybe_with_trailing_slash = try allocator.dupe(u8, source_directories_splitted.first());
833
834 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)) {
835 allocator.free(msvc_include_dir_maybe_with_trailing_slash);
836 return error.PathNotFound;
837 }
838
839 var msvc_dir = std.ArrayList(u8).fromOwnedSlice(allocator, msvc_include_dir_maybe_with_trailing_slash);
840 errdefer msvc_dir.deinit();
841
842 // String might contain trailing slash, so trim it here
843 if (msvc_dir.items.len > "C:\\".len and msvc_dir.getLast() == '\\') _ = msvc_dir.pop();
844
845 // Remove `\include` at the end of path
846 if (std.mem.endsWith(u8, msvc_dir.items, "\\include")) {
847 msvc_dir.shrinkRetainingCapacity(msvc_dir.items.len - "\\include".len);
848 }
849
850 const folder_with_arch = "\\Lib\\" ++ comptime switch (builtin.target.cpu.arch) {
851 .x86 => "x86",
852 .x86_64 => "x64",
853 .arm, .armeb => "arm",
854 .aarch64 => "arm64",
855 else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag),
856 };
857
858 try msvc_dir.appendSlice(folder_with_arch);
859 const msvc_dir_with_arch = try msvc_dir.toOwnedSlice();
860 break :msvc_dir msvc_dir_with_arch;
861 };
862 errdefer allocator.free(msvc_dir);
863
864 if (!verifyLibDir(msvc_dir)) {
865 return error.PathNotFound;
866 }
867
868 return msvc_dir;
869 }
870
871 fn findViaVs7Key(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
872 var base_path: std.ArrayList(u8) = base_path: {
873 try_env: {
874 var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
875 error.OutOfMemory => return error.OutOfMemory,
876 else => break :try_env,
877 };
878 defer env_map.deinit();
879
880 if (env_map.get("VS140COMNTOOLS")) |VS140COMNTOOLS| {
881 if (VS140COMNTOOLS.len < "C:\\Common7\\Tools".len) break :try_env;
882 if (!std.fs.path.isAbsolute(VS140COMNTOOLS)) break :try_env;
883 var list = std.ArrayList(u8).init(allocator);
884 errdefer list.deinit();
885
886 try list.appendSlice(VS140COMNTOOLS); // C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools
887 // String might contain trailing slash, so trim it here
888 if (list.items.len > "C:\\".len and list.getLast() == '\\') _ = list.pop();
889 list.shrinkRetainingCapacity(list.items.len - "\\Common7\\Tools".len); // C:\Program Files (x86)\Microsoft Visual Studio 14.0
890 break :base_path list;
891 }
892 }
893
894 const vs7_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7") catch return error.PathNotFound;
895 defer vs7_key.closeKey();
896 try_vs7_key: {
897 const path_maybe_with_trailing_slash = vs7_key.getString(allocator, "", "14.0") catch |err| switch (err) {
898 error.OutOfMemory => return error.OutOfMemory,
899 else => break :try_vs7_key,
900 };
901
902 if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
903 allocator.free(path_maybe_with_trailing_slash);
904 break :try_vs7_key;
905 }
906
907 var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
908 errdefer path.deinit();
909
910 // String might contain trailing slash, so trim it here
911 if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
912 break :base_path path;
913 }
914 return error.PathNotFound;
915 };
916 errdefer base_path.deinit();
917
918 const folder_with_arch = "\\VC\\lib\\" ++ comptime switch (builtin.target.cpu.arch) {
919 .x86 => "", //x86 is in the root of the Lib folder
920 .x86_64 => "amd64",
921 .arm, .armeb => "arm",
922 .aarch64 => "arm64",
923 else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag),
924 };
925 try base_path.appendSlice(folder_with_arch);
926
927 if (!verifyLibDir(base_path.items)) {
928 return error.PathNotFound;
929 }
930
931 const full_path = try base_path.toOwnedSlice();
932 return full_path;
933 }
934
935 fn verifyLibDir(lib_dir_path: []const u8) bool {
936 std.debug.assert(std.fs.path.isAbsolute(lib_dir_path)); // should be already handled in `findVia*`
937
938 var dir = std.fs.openDirAbsolute(lib_dir_path, .{}) catch return false;
939 defer dir.close();
940
941 const stat = dir.statFile("vcruntime.lib") catch return false;
942 if (stat.kind != .file)
943 return false;
944
945 return true;
946 }
947
948 /// Find path to MSVC's `lib/` directory.
949 /// Caller owns the result.
950 pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, MsvcLibDirNotFound }![]const u8 {
951 const full_path = MsvcLibDir.findViaCOM(allocator) catch |err1| switch (err1) {
952 error.OutOfMemory => return error.OutOfMemory,
953 error.PathNotFound => MsvcLibDir.findViaRegistry(allocator) catch |err2| switch (err2) {
954 error.OutOfMemory => return error.OutOfMemory,
955 error.PathNotFound => MsvcLibDir.findViaVs7Key(allocator) catch |err3| switch (err3) {
956 error.OutOfMemory => return error.OutOfMemory,
957 error.PathNotFound => return error.MsvcLibDirNotFound,
958 },
959 },
960 };
961 errdefer allocator.free(full_path);
962
963 return full_path;
964 }
965};
stage1/wasi.c+5-3
......@@ -662,13 +662,15 @@ uint32_t wasi_snapshot_preview1_fd_filestat_set_times(uint32_t fd, uint64_t atim
662662}
663663
664664uint32_t wasi_snapshot_preview1_environ_sizes_get(uint32_t environ_size, uint32_t environ_buf_size) {
665 (void)environ_size;
666 (void)environ_buf_size;
665 uint8_t *const m = *wasm_memory;
666 uint32_t *environ_size_ptr = (uint32_t *)&m[environ_size];
667 uint32_t *environ_buf_size_ptr = (uint32_t *)&m[environ_buf_size];
667668#if LOG_TRACE
668669 fprintf(stderr, "wasi_snapshot_preview1_environ_sizes_get()\n");
669670#endif
670671
671 panic("unimplemented");
672 *environ_size_ptr = 0;
673 *environ_buf_size_ptr = 0;
672674 return wasi_errno_success;
673675}
674676