| author | |
| committer | |
| log | a4380a30f5aeafd03e909164b28550e5960500f9 |
| tree | 8497854da5a7b365189bcc592aa33b3730e00901 |
| parent | 6e078883eebd0532928dba0c70e86a2eee0f246b |
part of #19063
This is a prerequisite for doing the same for Resinator.21 files changed, 2395 insertions(+), 2377 deletions(-)
CMakeLists.txt+5-5| ... | @@ -507,16 +507,18 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -507,16 +507,18 @@ set(ZIG_STAGE2_SOURCES |
| 507 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Ast.zig" | 507 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Ast.zig" |
| 508 | "${CMAKE_SOURCE_DIR}/lib/std/zig/AstGen.zig" | 508 | "${CMAKE_SOURCE_DIR}/lib/std/zig/AstGen.zig" |
| 509 | "${CMAKE_SOURCE_DIR}/lib/std/zig/AstRlAnnotate.zig" | 509 | "${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" |
| 511 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig" | 511 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig" |
| 512 | "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig" | ||
| 513 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Server.zig" | 512 | "${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" | ||
| 514 | "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig" | 517 | "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig" |
| 515 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig" | 518 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig" |
| 516 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig" | 519 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig" |
| 517 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system/x86.zig" | 520 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system/x86.zig" |
| 518 | "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig" | 521 | "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig" |
| 519 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Zir.zig" | ||
| 520 | "${CMAKE_SOURCE_DIR}/src/Air.zig" | 522 | "${CMAKE_SOURCE_DIR}/src/Air.zig" |
| 521 | "${CMAKE_SOURCE_DIR}/src/Compilation.zig" | 523 | "${CMAKE_SOURCE_DIR}/src/Compilation.zig" |
| 522 | "${CMAKE_SOURCE_DIR}/src/Compilation/Config.zig" | 524 | "${CMAKE_SOURCE_DIR}/src/Compilation/Config.zig" |
| ... | @@ -570,7 +572,6 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -570,7 +572,6 @@ set(ZIG_STAGE2_SOURCES |
| 570 | "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig" | 572 | "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig" |
| 571 | "${CMAKE_SOURCE_DIR}/src/glibc.zig" | 573 | "${CMAKE_SOURCE_DIR}/src/glibc.zig" |
| 572 | "${CMAKE_SOURCE_DIR}/src/introspect.zig" | 574 | "${CMAKE_SOURCE_DIR}/src/introspect.zig" |
| 573 | "${CMAKE_SOURCE_DIR}/src/libc_installation.zig" | ||
| 574 | "${CMAKE_SOURCE_DIR}/src/libcxx.zig" | 575 | "${CMAKE_SOURCE_DIR}/src/libcxx.zig" |
| 575 | "${CMAKE_SOURCE_DIR}/src/libtsan.zig" | 576 | "${CMAKE_SOURCE_DIR}/src/libtsan.zig" |
| 576 | "${CMAKE_SOURCE_DIR}/src/libunwind.zig" | 577 | "${CMAKE_SOURCE_DIR}/src/libunwind.zig" |
| ... | @@ -645,7 +646,6 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -645,7 +646,6 @@ set(ZIG_STAGE2_SOURCES |
| 645 | "${CMAKE_SOURCE_DIR}/src/translate_c/ast.zig" | 646 | "${CMAKE_SOURCE_DIR}/src/translate_c/ast.zig" |
| 646 | "${CMAKE_SOURCE_DIR}/src/type.zig" | 647 | "${CMAKE_SOURCE_DIR}/src/type.zig" |
| 647 | "${CMAKE_SOURCE_DIR}/src/wasi_libc.zig" | 648 | "${CMAKE_SOURCE_DIR}/src/wasi_libc.zig" |
| 648 | "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig" | ||
| 649 | "${CMAKE_SOURCE_DIR}/src/stubs/aro_builtins.zig" | 649 | "${CMAKE_SOURCE_DIR}/src/stubs/aro_builtins.zig" |
| 650 | "${CMAKE_SOURCE_DIR}/src/stubs/aro_names.zig" | 650 | "${CMAKE_SOURCE_DIR}/src/stubs/aro_names.zig" |
| 651 | ) | 651 | ) |
lib/compiler/libc.zig created+137| ... | @@ -0,0 +1,137 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const mem = std.mem; | ||
| 3 | const io = std.io; | ||
| 4 | const LibCInstallation = std.zig.LibCInstallation; | ||
| 5 | |||
| 6 | const 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 | |||
| 25 | pub 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 | |||
| 134 | fn 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| ... | @@ -2755,6 +2755,22 @@ fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool { | ... | @@ -2755,6 +2755,22 @@ fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool { |
| 2755 | } | 2755 | } |
| 2756 | } | 2756 | } |
| 2757 | 2757 | ||
| 2758 | pub fn osArchName(target: std.Target) [:0]const u8 { | ||
| 2759 | return switch (target.os.tag) { | ||
| 2760 | .linux => switch (target.cpu.arch) { | ||
| 2761 | .arm, .armeb, .thumb, .thumbeb => "arm", | ||
| 2762 | .aarch64, .aarch64_be, .aarch64_32 => "aarch64", | ||
| 2763 | .mips, .mipsel, .mips64, .mips64el => "mips", | ||
| 2764 | .powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc", | ||
| 2765 | .riscv32, .riscv64 => "riscv", | ||
| 2766 | .sparc, .sparcel, .sparc64 => "sparc", | ||
| 2767 | .x86, .x86_64 => "x86", | ||
| 2768 | else => @tagName(target.cpu.arch), | ||
| 2769 | }, | ||
| 2770 | else => @tagName(target.cpu.arch), | ||
| 2771 | }; | ||
| 2772 | } | ||
| 2773 | |||
| 2758 | const Target = @This(); | 2774 | const Target = @This(); |
| 2759 | const std = @import("std.zig"); | 2775 | const std = @import("std.zig"); |
| 2760 | const builtin = @import("builtin"); | 2776 | const builtin = @import("builtin"); |
lib/std/zig.zig+119-13| ... | @@ -14,6 +14,10 @@ pub const system = @import("zig/system.zig"); | ... | @@ -14,6 +14,10 @@ pub const system = @import("zig/system.zig"); |
| 14 | pub const CrossTarget = std.Target.Query; | 14 | pub const CrossTarget = std.Target.Query; |
| 15 | pub const BuiltinFn = @import("zig/BuiltinFn.zig"); | 15 | pub const BuiltinFn = @import("zig/BuiltinFn.zig"); |
| 16 | pub const AstRlAnnotate = @import("zig/AstRlAnnotate.zig"); | 16 | pub const AstRlAnnotate = @import("zig/AstRlAnnotate.zig"); |
| 17 | pub const LibCInstallation = @import("zig/LibCInstallation.zig"); | ||
| 18 | pub const WindowsSdk = @import("zig/WindowsSdk.zig"); | ||
| 19 | pub const LibCDirs = @import("zig/LibCDirs.zig"); | ||
| 20 | pub const target = @import("zig/target.zig"); | ||
| 17 | 21 | ||
| 18 | // Character literal parsing | 22 | // Character literal parsing |
| 19 | pub const ParsedCharLiteral = string_literal.ParsedCharLiteral; | 23 | pub const ParsedCharLiteral = string_literal.ParsedCharLiteral; |
| ... | @@ -142,10 +146,10 @@ pub const BinNameOptions = struct { | ... | @@ -142,10 +146,10 @@ pub const BinNameOptions = struct { |
| 142 | /// Returns the standard file system basename of a binary generated by the Zig compiler. | 146 | /// Returns the standard file system basename of a binary generated by the Zig compiler. |
| 143 | pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 { | 147 | pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 { |
| 144 | const root_name = options.root_name; | 148 | const root_name = options.root_name; |
| 145 | const target = options.target; | 149 | const t = options.target; |
| 146 | switch (target.ofmt) { | 150 | switch (t.ofmt) { |
| 147 | .coff => switch (options.output_mode) { | 151 | .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() }), |
| 149 | .Lib => { | 153 | .Lib => { |
| 150 | const suffix = switch (options.link_mode orelse .Static) { | 154 | const suffix = switch (options.link_mode orelse .Static) { |
| 151 | .Static => ".lib", | 155 | .Static => ".lib", |
| ... | @@ -160,16 +164,16 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe | ... | @@ -160,16 +164,16 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe |
| 160 | .Lib => { | 164 | .Lib => { |
| 161 | switch (options.link_mode orelse .Static) { | 165 | switch (options.link_mode orelse .Static) { |
| 162 | .Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{ | 166 | .Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{ |
| 163 | target.libPrefix(), root_name, | 167 | t.libPrefix(), root_name, |
| 164 | }), | 168 | }), |
| 165 | .Dynamic => { | 169 | .Dynamic => { |
| 166 | if (options.version) |ver| { | 170 | if (options.version) |ver| { |
| 167 | return std.fmt.allocPrint(allocator, "{s}{s}.so.{d}.{d}.{d}", .{ | 171 | 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, |
| 169 | }); | 173 | }); |
| 170 | } else { | 174 | } else { |
| 171 | return std.fmt.allocPrint(allocator, "{s}{s}.so", .{ | 175 | return std.fmt.allocPrint(allocator, "{s}{s}.so", .{ |
| 172 | target.libPrefix(), root_name, | 176 | t.libPrefix(), root_name, |
| 173 | }); | 177 | }); |
| 174 | } | 178 | } |
| 175 | }, | 179 | }, |
| ... | @@ -182,16 +186,16 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe | ... | @@ -182,16 +186,16 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe |
| 182 | .Lib => { | 186 | .Lib => { |
| 183 | switch (options.link_mode orelse .Static) { | 187 | switch (options.link_mode orelse .Static) { |
| 184 | .Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{ | 188 | .Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{ |
| 185 | target.libPrefix(), root_name, | 189 | t.libPrefix(), root_name, |
| 186 | }), | 190 | }), |
| 187 | .Dynamic => { | 191 | .Dynamic => { |
| 188 | if (options.version) |ver| { | 192 | if (options.version) |ver| { |
| 189 | return std.fmt.allocPrint(allocator, "{s}{s}.{d}.{d}.{d}.dylib", .{ | 193 | 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, |
| 191 | }); | 195 | }); |
| 192 | } else { | 196 | } else { |
| 193 | return std.fmt.allocPrint(allocator, "{s}{s}.dylib", .{ | 197 | return std.fmt.allocPrint(allocator, "{s}{s}.dylib", .{ |
| 194 | target.libPrefix(), root_name, | 198 | t.libPrefix(), root_name, |
| 195 | }); | 199 | }); |
| 196 | } | 200 | } |
| 197 | }, | 201 | }, |
| ... | @@ -200,11 +204,11 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe | ... | @@ -200,11 +204,11 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe |
| 200 | .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}), | 204 | .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}), |
| 201 | }, | 205 | }, |
| 202 | .wasm => switch (options.output_mode) { | 206 | .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() }), |
| 204 | .Lib => { | 208 | .Lib => { |
| 205 | switch (options.link_mode orelse .Static) { | 209 | switch (options.link_mode orelse .Static) { |
| 206 | .Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{ | 210 | .Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{ |
| 207 | target.libPrefix(), root_name, | 211 | t.libPrefix(), root_name, |
| 208 | }), | 212 | }), |
| 209 | .Dynamic => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}), | 213 | .Dynamic => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}), |
| 210 | } | 214 | } |
| ... | @@ -218,10 +222,10 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe | ... | @@ -218,10 +222,10 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe |
| 218 | .plan9 => switch (options.output_mode) { | 222 | .plan9 => switch (options.output_mode) { |
| 219 | .Exe => return allocator.dupe(u8, root_name), | 223 | .Exe => return allocator.dupe(u8, root_name), |
| 220 | .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ | 224 | .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), |
| 222 | }), | 226 | }), |
| 223 | .Lib => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{ | 227 | .Lib => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{ |
| 224 | target.libPrefix(), root_name, | 228 | t.libPrefix(), root_name, |
| 225 | }), | 229 | }), |
| 226 | }, | 230 | }, |
| 227 | .nvptx => return std.fmt.allocPrint(allocator, "{s}.ptx", .{root_name}), | 231 | .nvptx => return std.fmt.allocPrint(allocator, "{s}.ptx", .{root_name}), |
| ... | @@ -900,15 +904,117 @@ pub fn putAstErrorsIntoBundle( | ... | @@ -900,15 +904,117 @@ pub fn putAstErrorsIntoBundle( |
| 900 | try wip_errors.addZirErrorMessages(zir, tree, tree.source, path); | 904 | try wip_errors.addZirErrorMessages(zir, tree, tree.source, path); |
| 901 | } | 905 | } |
| 902 | 906 | ||
| 907 | pub 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 | |||
| 912 | pub 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 | |||
| 965 | pub 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`. | ||
| 972 | pub 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 | |||
| 903 | test { | 1005 | test { |
| 904 | _ = Ast; | 1006 | _ = Ast; |
| 905 | _ = AstRlAnnotate; | 1007 | _ = AstRlAnnotate; |
| 906 | _ = BuiltinFn; | 1008 | _ = BuiltinFn; |
| 907 | _ = Client; | 1009 | _ = Client; |
| 908 | _ = ErrorBundle; | 1010 | _ = ErrorBundle; |
| 1011 | _ = LibCDirs; | ||
| 1012 | _ = LibCInstallation; | ||
| 909 | _ = Server; | 1013 | _ = Server; |
| 1014 | _ = WindowsSdk; | ||
| 910 | _ = number_literal; | 1015 | _ = number_literal; |
| 911 | _ = primitives; | 1016 | _ = primitives; |
| 912 | _ = string_literal; | 1017 | _ = string_literal; |
| 913 | _ = system; | 1018 | _ = system; |
| 1019 | _ = target; | ||
| 914 | } | 1020 | } |
lib/std/zig/LibCDirs.zig created+281| ... | @@ -0,0 +1,281 @@ | ||
| 1 | libc_include_dir_list: []const []const u8, | ||
| 2 | libc_installation: ?*const LibCInstallation, | ||
| 3 | libc_framework_dir_list: []const []const u8, | ||
| 4 | sysroot: ?[]const u8, | ||
| 5 | darwin_sdk_layout: ?DarwinSdkLayout, | ||
| 6 | |||
| 7 | /// The filesystem layout of darwin SDK elements. | ||
| 8 | pub 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 | |||
| 15 | pub 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 | |||
| 91 | fn 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 | |||
| 146 | pub 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 | |||
| 222 | fn 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 | |||
| 277 | const LibCDirs = @This(); | ||
| 278 | const builtin = @import("builtin"); | ||
| 279 | const std = @import("../std.zig"); | ||
| 280 | const LibCInstallation = std.zig.LibCInstallation; | ||
| 281 | const 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 | |||
| 3 | include_dir: ?[]const u8 = null, | ||
| 4 | sys_include_dir: ?[]const u8 = null, | ||
| 5 | crt_dir: ?[]const u8 = null, | ||
| 6 | msvc_lib_dir: ?[]const u8 = null, | ||
| 7 | kernel32_lib_dir: ?[]const u8 = null, | ||
| 8 | gcc_dir: ?[]const u8 = null, | ||
| 9 | |||
| 10 | pub 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 | |||
| 26 | pub 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 | |||
| 112 | pub 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 | |||
| 158 | pub 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. | ||
| 167 | pub 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`. | ||
| 220 | pub 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 | |||
| 230 | fn 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 | |||
| 358 | fn 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 | |||
| 397 | fn 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 | |||
| 443 | fn 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 | |||
| 452 | fn 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 | |||
| 461 | fn 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 | |||
| 508 | fn 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 | |||
| 540 | fn 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 | |||
| 550 | pub 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 | ||
| 558 | fn 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 | |||
| 631 | fn 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 | |||
| 654 | const Search = struct { | ||
| 655 | path: []const u8, | ||
| 656 | version: []const u8, | ||
| 657 | }; | ||
| 658 | |||
| 659 | fn 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 | |||
| 678 | const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS"; | ||
| 679 | |||
| 680 | fn 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 | |||
| 698 | const LibCInstallation = @This(); | ||
| 699 | const std = @import("std"); | ||
| 700 | const builtin = @import("builtin"); | ||
| 701 | const Target = std.Target; | ||
| 702 | const fs = std.fs; | ||
| 703 | const Allocator = std.mem.Allocator; | ||
| 704 | |||
| 705 | const is_darwin = builtin.target.isDarwin(); | ||
| 706 | const is_windows = builtin.target.os.tag == .windows; | ||
| 707 | const is_haiku = builtin.target.os.tag == .haiku; | ||
| 708 | |||
| 709 | const log = std.log.scoped(.libc_installation); | ||
lib/std/zig/WindowsSdk.zig created+964| ... | @@ -0,0 +1,964 @@ | ||
| 1 | windows10sdk: ?Windows10Sdk, | ||
| 2 | windows81sdk: ?Windows81Sdk, | ||
| 3 | msvc_lib_dir: ?[]const u8, | ||
| 4 | |||
| 5 | const WindowsSdk = @This(); | ||
| 6 | const std = @import("std"); | ||
| 7 | const builtin = @import("builtin"); | ||
| 8 | |||
| 9 | const windows = std.os.windows; | ||
| 10 | const RRF = windows.advapi32.RRF; | ||
| 11 | |||
| 12 | const WINDOWS_KIT_REG_KEY = "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots"; | ||
| 13 | |||
| 14 | // https://learn.microsoft.com/en-us/windows/win32/msi/productversion | ||
| 15 | const 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 | ||
| 17 | const 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)`. | ||
| 22 | pub 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 | |||
| 70 | pub 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. | ||
| 85 | fn 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 | |||
| 155 | const 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 | |||
| 246 | const 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 | |||
| 412 | pub 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 | |||
| 508 | pub 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 | |||
| 579 | const 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 @@ | ||
| 1 | pub 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 | |||
| 11 | pub 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 | |||
| 74 | pub 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 | |||
| 93 | pub 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 | |||
| 100 | pub 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 | |||
| 117 | const std = @import("std"); | ||
src/Compilation.zig+10-228| ... | @@ -19,7 +19,7 @@ const link = @import("link.zig"); | ... | @@ -19,7 +19,7 @@ const link = @import("link.zig"); |
| 19 | const tracy = @import("tracy.zig"); | 19 | const tracy = @import("tracy.zig"); |
| 20 | const trace = tracy.trace; | 20 | const trace = tracy.trace; |
| 21 | const build_options = @import("build_options"); | 21 | const build_options = @import("build_options"); |
| 22 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | 22 | const LibCInstallation = std.zig.LibCInstallation; |
| 23 | const glibc = @import("glibc.zig"); | 23 | const glibc = @import("glibc.zig"); |
| 24 | const musl = @import("musl.zig"); | 24 | const musl = @import("musl.zig"); |
| 25 | const mingw = @import("mingw.zig"); | 25 | const mingw = @import("mingw.zig"); |
| ... | @@ -1232,7 +1232,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1232,7 +1232,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1232 | 1232 | ||
| 1233 | const link_libc = options.config.link_libc; | 1233 | const link_libc = options.config.link_libc; |
| 1234 | 1234 | ||
| 1235 | const libc_dirs = try detectLibCIncludeDirs( | 1235 | const libc_dirs = try std.zig.LibCDirs.detect( |
| 1236 | arena, | 1236 | arena, |
| 1237 | options.zig_lib_directory.path.?, | 1237 | options.zig_lib_directory.path.?, |
| 1238 | options.root_mod.resolved_target.result, | 1238 | options.root_mod.resolved_target.result, |
| ... | @@ -1250,7 +1250,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1250,7 +1250,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1250 | // only relevant differences would be things like `#define` constants being | 1250 | // only relevant differences would be things like `#define` constants being |
| 1251 | // different in the MinGW headers vs the MSVC headers, but any such | 1251 | // different in the MinGW headers vs the MSVC headers, but any such |
| 1252 | // differences would likely be a MinGW bug. | 1252 | // differences would likely be a MinGW bug. |
| 1253 | const rc_dirs = b: { | 1253 | const rc_dirs: std.zig.LibCDirs = b: { |
| 1254 | // Set the includes to .none here when there are no rc files to compile | 1254 | // Set the includes to .none here when there are no rc files to compile |
| 1255 | var includes = if (options.rc_source_files.len > 0) options.rc_includes else .none; | 1255 | var includes = if (options.rc_source_files.len > 0) options.rc_includes else .none; |
| 1256 | const target = options.root_mod.resolved_target.result; | 1256 | const target = options.root_mod.resolved_target.result; |
| ... | @@ -1265,7 +1265,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1265,7 +1265,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1265 | } | 1265 | } |
| 1266 | } | 1266 | } |
| 1267 | while (true) switch (includes) { | 1267 | while (true) switch (includes) { |
| 1268 | .any, .msvc => break :b detectLibCIncludeDirs( | 1268 | .any, .msvc => break :b std.zig.LibCDirs.detect( |
| 1269 | arena, | 1269 | arena, |
| 1270 | options.zig_lib_directory.path.?, | 1270 | options.zig_lib_directory.path.?, |
| 1271 | .{ | 1271 | .{ |
| ... | @@ -1287,13 +1287,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1287,13 +1287,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1287 | } | 1287 | } |
| 1288 | return err; | 1288 | return err; |
| 1289 | }, | 1289 | }, |
| 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.?, .{ |
| 1291 | .cpu = target.cpu, | 1291 | .cpu = target.cpu, |
| 1292 | .os = target.os, | 1292 | .os = target.os, |
| 1293 | .abi = .gnu, | 1293 | .abi = .gnu, |
| 1294 | .ofmt = target.ofmt, | 1294 | .ofmt = target.ofmt, |
| 1295 | }), | 1295 | }), |
| 1296 | .none => break :b LibCDirs{ | 1296 | .none => break :b .{ |
| 1297 | .libc_include_dir_list = &[0][]u8{}, | 1297 | .libc_include_dir_list = &[0][]u8{}, |
| 1298 | .libc_installation = null, | 1298 | .libc_installation = null, |
| 1299 | .libc_framework_dir_list = &.{}, | 1299 | .libc_framework_dir_list = &.{}, |
| ... | @@ -1772,7 +1772,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1772,7 +1772,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1772 | // If we need to build glibc for the target, add work items for it. | 1772 | // If we need to build glibc for the target, add work items for it. |
| 1773 | // We go through the work queue so that building can be done in parallel. | 1773 | // We go through the work queue so that building can be done in parallel. |
| 1774 | if (comp.wantBuildGLibCFromSource()) { | 1774 | if (comp.wantBuildGLibCFromSource()) { |
| 1775 | if (!target_util.canBuildLibC(target)) return error.LibCUnavailable; | 1775 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; |
| 1776 | 1776 | ||
| 1777 | if (glibc.needsCrtiCrtn(target)) { | 1777 | if (glibc.needsCrtiCrtn(target)) { |
| 1778 | try comp.work_queue.write(&[_]Job{ | 1778 | try comp.work_queue.write(&[_]Job{ |
| ... | @@ -1787,7 +1787,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1787,7 +1787,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1787 | }); | 1787 | }); |
| 1788 | } | 1788 | } |
| 1789 | if (comp.wantBuildMuslFromSource()) { | 1789 | if (comp.wantBuildMuslFromSource()) { |
| 1790 | if (!target_util.canBuildLibC(target)) return error.LibCUnavailable; | 1790 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; |
| 1791 | 1791 | ||
| 1792 | try comp.work_queue.ensureUnusedCapacity(6); | 1792 | try comp.work_queue.ensureUnusedCapacity(6); |
| 1793 | if (musl.needsCrtiCrtn(target)) { | 1793 | if (musl.needsCrtiCrtn(target)) { |
| ... | @@ -1808,7 +1808,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1808,7 +1808,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1808 | } | 1808 | } |
| 1809 | 1809 | ||
| 1810 | if (comp.wantBuildWasiLibcFromSource()) { | 1810 | if (comp.wantBuildWasiLibcFromSource()) { |
| 1811 | if (!target_util.canBuildLibC(target)) return error.LibCUnavailable; | 1811 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; |
| 1812 | 1812 | ||
| 1813 | // worst-case we need all components | 1813 | // worst-case we need all components |
| 1814 | try comp.work_queue.ensureUnusedCapacity(comp.wasi_emulated_libs.len + 2); | 1814 | 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 | ... | @@ -1825,7 +1825,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1825 | } | 1825 | } |
| 1826 | 1826 | ||
| 1827 | if (comp.wantBuildMinGWFromSource()) { | 1827 | if (comp.wantBuildMinGWFromSource()) { |
| 1828 | if (!target_util.canBuildLibC(target)) return error.LibCUnavailable; | 1828 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; |
| 1829 | 1829 | ||
| 1830 | const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o }; | 1830 | const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o }; |
| 1831 | try comp.work_queue.ensureUnusedCapacity(2); | 1831 | try comp.work_queue.ensureUnusedCapacity(2); |
| ... | @@ -5830,224 +5830,6 @@ test "classifyFileExt" { | ... | @@ -5830,224 +5830,6 @@ test "classifyFileExt" { |
| 5830 | try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig")); | 5830 | try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig")); |
| 5831 | } | 5831 | } |
| 5832 | 5832 | ||
| 5833 | const 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 | |||
| 5841 | fn 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 | |||
| 5858 | pub 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 | |||
| 5934 | fn 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 | |||
| 5989 | fn 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 | |||
| 6051 | pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 { | 5833 | pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 { |
| 6052 | if (comp.wantBuildGLibCFromSource() or | 5834 | if (comp.wantBuildGLibCFromSource() or |
| 6053 | comp.wantBuildMuslFromSource() or | 5835 | comp.wantBuildMuslFromSource() or |
src/glibc.zig+4-5| ... | @@ -7,7 +7,6 @@ const path = fs.path; | ... | @@ -7,7 +7,6 @@ const path = fs.path; |
| 7 | const assert = std.debug.assert; | 7 | const assert = std.debug.assert; |
| 8 | const Version = std.SemanticVersion; | 8 | const Version = std.SemanticVersion; |
| 9 | 9 | ||
| 10 | const target_util = @import("target.zig"); | ||
| 11 | const Compilation = @import("Compilation.zig"); | 10 | const Compilation = @import("Compilation.zig"); |
| 12 | const build_options = @import("build_options"); | 11 | const build_options = @import("build_options"); |
| 13 | const trace = @import("tracy.zig").trace; | 12 | const trace = @import("tracy.zig").trace; |
| ... | @@ -21,7 +20,7 @@ pub const Lib = struct { | ... | @@ -21,7 +20,7 @@ pub const Lib = struct { |
| 21 | 20 | ||
| 22 | pub const ABI = struct { | 21 | pub const ABI = struct { |
| 23 | all_versions: []const Version, // all defined versions (one abilist from v2.0.0 up to current) | 22 | 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, |
| 25 | /// The bytes from the file verbatim, starting from the u16 number | 24 | /// The bytes from the file verbatim, starting from the u16 number |
| 26 | /// of function inclusions. | 25 | /// of function inclusions. |
| 27 | inclusions: []const u8, | 26 | inclusions: []const u8, |
| ... | @@ -103,7 +102,7 @@ pub fn loadMetaData(gpa: Allocator, contents: []const u8) LoadMetaDataError!*ABI | ... | @@ -103,7 +102,7 @@ pub fn loadMetaData(gpa: Allocator, contents: []const u8) LoadMetaDataError!*ABI |
| 103 | const targets_len = contents[index]; | 102 | const targets_len = contents[index]; |
| 104 | index += 1; | 103 | index += 1; |
| 105 | 104 | ||
| 106 | const targets = try arena.alloc(target_util.ArchOsAbi, targets_len); | 105 | const targets = try arena.alloc(std.zig.target.ArchOsAbi, targets_len); |
| 107 | var i: u8 = 0; | 106 | var i: u8 = 0; |
| 108 | while (i < targets.len) : (i += 1) { | 107 | while (i < targets.len) : (i += 1) { |
| 109 | const target_name = mem.sliceTo(contents[index..], 0); | 108 | const target_name = mem.sliceTo(contents[index..], 0); |
| ... | @@ -512,7 +511,7 @@ fn add_include_dirs(comp: *Compilation, arena: Allocator, args: *std.ArrayList([ | ... | @@ -512,7 +511,7 @@ fn add_include_dirs(comp: *Compilation, arena: Allocator, args: *std.ArrayList([ |
| 512 | try args.append("-I"); | 511 | try args.append("-I"); |
| 513 | try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "generic-glibc")); | 512 | try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "generic-glibc")); |
| 514 | 513 | ||
| 515 | const arch_name = target_util.osArchName(target); | 514 | const arch_name = target.osArchName(); |
| 516 | try args.append("-I"); | 515 | try args.append("-I"); |
| 517 | try args.append(try std.fmt.allocPrint(arena, "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-linux-any", .{ | 516 | try args.append(try std.fmt.allocPrint(arena, "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-linux-any", .{ |
| 518 | comp.zig_lib_directory.path.?, arch_name, | 517 | comp.zig_lib_directory.path.?, arch_name, |
| ... | @@ -726,7 +725,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: *std.Progress.Node) !vo | ... | @@ -726,7 +725,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: *std.Progress.Node) !vo |
| 726 | break i; | 725 | break i; |
| 727 | } | 726 | } |
| 728 | } else { | 727 | } else { |
| 729 | unreachable; // target_util.available_libcs prevents us from getting here | 728 | unreachable; // std.zig.target.available_libcs prevents us from getting here |
| 730 | }; | 729 | }; |
| 731 | 730 | ||
| 732 | const target_ver_index = for (metadata.all_versions, 0..) |ver, i| { | 731 | 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 { | ... | @@ -84,14 +84,14 @@ pub fn resolveGlobalCacheDir(allocator: mem.Allocator) ![]u8 { |
| 84 | if (builtin.os.tag == .wasi) | 84 | if (builtin.os.tag == .wasi) |
| 85 | @compileError("on WASI the global cache dir must be resolved with preopens"); | 85 | @compileError("on WASI the global cache dir must be resolved with preopens"); |
| 86 | 86 | ||
| 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; |
| 88 | 88 | ||
| 89 | const appname = "zig"; | 89 | const appname = "zig"; |
| 90 | 90 | ||
| 91 | if (builtin.os.tag != .windows) { | 91 | 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| { |
| 93 | return fs.path.join(allocator, &[_][]const u8{ cache_root, appname }); | 93 | 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| { |
| 95 | return fs.path.join(allocator, &[_][]const u8{ home, ".cache", appname }); | 95 | return fs.path.join(allocator, &[_][]const u8{ home, ".cache", appname }); |
| 96 | } | 96 | } |
| 97 | } | 97 | } |
| ... | @@ -141,41 +141,3 @@ pub fn resolvePath( | ... | @@ -141,41 +141,3 @@ pub fn resolvePath( |
| 141 | pub fn isUpDir(p: []const u8) bool { | 141 | pub fn isUpDir(p: []const u8) bool { |
| 142 | return mem.startsWith(u8, p, "..") and (p.len == 2 or p[2] == fs.path.sep); | 142 | return mem.startsWith(u8, p, "..") and (p.len == 2 or p[2] == fs.path.sep); |
| 143 | } | 143 | } |
| 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`. | ||
| 147 | pub 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const Target = std.Target; | ||
| 4 | const fs = std.fs; | ||
| 5 | const Allocator = std.mem.Allocator; | ||
| 6 | |||
| 7 | const is_darwin = builtin.target.isDarwin(); | ||
| 8 | const is_windows = builtin.target.os.tag == .windows; | ||
| 9 | const is_haiku = builtin.target.os.tag == .haiku; | ||
| 10 | |||
| 11 | const log = std.log.scoped(.libc_installation); | ||
| 12 | |||
| 13 | const ZigWindowsSDK = @import("windows_sdk.zig").ZigWindowsSDK; | ||
| 14 | const EnvVar = @import("introspect.zig").EnvVar; | ||
| 15 | |||
| 16 | /// See the render function implementation for documentation of the fields. | ||
| 17 | pub 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 | |||
| 566 | pub 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 | ||
| 574 | fn 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 | |||
| 647 | fn 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 | |||
| 670 | const Search = struct { | ||
| 671 | path: []const u8, | ||
| 672 | version: []const u8, | ||
| 673 | }; | ||
| 674 | |||
| 675 | fn 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 | |||
| 694 | const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS"; | ||
| 695 | |||
| 696 | fn 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"); | ... | @@ -12,7 +12,7 @@ const Air = @import("Air.zig"); |
| 12 | const Allocator = std.mem.Allocator; | 12 | const Allocator = std.mem.Allocator; |
| 13 | const Cache = std.Build.Cache; | 13 | const Cache = std.Build.Cache; |
| 14 | const Compilation = @import("Compilation.zig"); | 14 | const Compilation = @import("Compilation.zig"); |
| 15 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | 15 | const LibCInstallation = std.zig.LibCInstallation; |
| 16 | const Liveness = @import("Liveness.zig"); | 16 | const Liveness = @import("Liveness.zig"); |
| 17 | const Module = @import("Module.zig"); | 17 | const Module = @import("Module.zig"); |
| 18 | const InternPool = @import("InternPool.zig"); | 18 | const InternPool = @import("InternPool.zig"); |
src/link/MachO.zig+1-7| ... | @@ -4616,13 +4616,7 @@ const SystemLib = struct { | ... | @@ -4616,13 +4616,7 @@ const SystemLib = struct { |
| 4616 | must_link: bool = false, | 4616 | must_link: bool = false, |
| 4617 | }; | 4617 | }; |
| 4618 | 4618 | ||
| 4619 | /// The filesystem layout of darwin SDK elements. | 4619 | pub const SdkLayout = std.zig.LibCDirs.DarwinSdkLayout; |
| 4620 | pub 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 | }; | ||
| 4626 | 4620 | ||
| 4627 | const UndefinedTreatment = enum { | 4621 | const UndefinedTreatment = enum { |
| 4628 | @"error", | 4622 | @"error", |
src/main.zig+22-206| ... | @@ -19,8 +19,8 @@ const link = @import("link.zig"); | ... | @@ -19,8 +19,8 @@ const link = @import("link.zig"); |
| 19 | const Package = @import("Package.zig"); | 19 | const Package = @import("Package.zig"); |
| 20 | const build_options = @import("build_options"); | 20 | const build_options = @import("build_options"); |
| 21 | const introspect = @import("introspect.zig"); | 21 | const introspect = @import("introspect.zig"); |
| 22 | const EnvVar = introspect.EnvVar; | 22 | const EnvVar = std.zig.EnvVar; |
| 23 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | 23 | const LibCInstallation = std.zig.LibCInstallation; |
| 24 | const wasi_libc = @import("wasi_libc.zig"); | 24 | const wasi_libc = @import("wasi_libc.zig"); |
| 25 | const Cache = std.Build.Cache; | 25 | const Cache = std.Build.Cache; |
| 26 | const target_util = @import("target.zig"); | 26 | const target_util = @import("target.zig"); |
| ... | @@ -294,17 +294,17 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { | ... | @@ -294,17 +294,17 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 294 | } else if (mem.eql(u8, cmd, "rc")) { | 294 | } else if (mem.eql(u8, cmd, "rc")) { |
| 295 | return cmdRc(gpa, arena, args[1..]); | 295 | return cmdRc(gpa, arena, args[1..]); |
| 296 | } else if (mem.eql(u8, cmd, "fmt")) { | 296 | } 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); |
| 298 | } else if (mem.eql(u8, cmd, "objcopy")) { | 298 | } else if (mem.eql(u8, cmd, "objcopy")) { |
| 299 | return @import("objcopy.zig").cmdObjCopy(gpa, arena, cmd_args); | 299 | return @import("objcopy.zig").cmdObjCopy(gpa, arena, cmd_args); |
| 300 | } else if (mem.eql(u8, cmd, "fetch")) { | 300 | } else if (mem.eql(u8, cmd, "fetch")) { |
| 301 | return cmdFetch(gpa, arena, cmd_args); | 301 | return cmdFetch(gpa, arena, cmd_args); |
| 302 | } else if (mem.eql(u8, cmd, "libc")) { | 302 | } else if (mem.eql(u8, cmd, "libc")) { |
| 303 | return cmdLibC(gpa, cmd_args); | 303 | return jitCmd(gpa, arena, cmd_args, "libc", "libc.zig", true); |
| 304 | } else if (mem.eql(u8, cmd, "init")) { | 304 | } else if (mem.eql(u8, cmd, "init")) { |
| 305 | return cmdInit(gpa, arena, cmd_args); | 305 | return cmdInit(gpa, arena, cmd_args); |
| 306 | } else if (mem.eql(u8, cmd, "targets")) { | 306 | } else if (mem.eql(u8, cmd, "targets")) { |
| 307 | const host = resolveTargetQueryOrFatal(.{}); | 307 | const host = std.zig.resolveTargetQueryOrFatal(.{}); |
| 308 | const stdout = io.getStdOut().writer(); | 308 | const stdout = io.getStdOut().writer(); |
| 309 | return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, host); | 309 | return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, host); |
| 310 | } else if (mem.eql(u8, cmd, "version")) { | 310 | } else if (mem.eql(u8, cmd, "version")) { |
| ... | @@ -317,7 +317,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { | ... | @@ -317,7 +317,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 317 | verifyLibcxxCorrectlyLinked(); | 317 | verifyLibcxxCorrectlyLinked(); |
| 318 | return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer()); | 318 | return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer()); |
| 319 | } else if (mem.eql(u8, cmd, "reduce")) { | 319 | } 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); |
| 321 | } else if (mem.eql(u8, cmd, "zen")) { | 321 | } else if (mem.eql(u8, cmd, "zen")) { |
| 322 | return io.getStdOut().writeAll(info_zen); | 322 | return io.getStdOut().writeAll(info_zen); |
| 323 | } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) { | 323 | } 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( | ... | @@ -3259,7 +3259,7 @@ fn buildOutputType( |
| 3259 | const triple_name = try target.zigTriple(arena); | 3259 | const triple_name = try target.zigTriple(arena); |
| 3260 | std.log.err("unable to find or provide libc for target '{s}'", .{triple_name}); | 3260 | std.log.err("unable to find or provide libc for target '{s}'", .{triple_name}); |
| 3261 | 3261 | ||
| 3262 | for (target_util.available_libcs) |t| { | 3262 | for (std.zig.target.available_libcs) |t| { |
| 3263 | if (t.arch == target.cpu.arch and t.os == target.os.tag) { | 3263 | if (t.arch == target.cpu.arch and t.os == target.os.tag) { |
| 3264 | if (t.os_ver) |os_ver| { | 3264 | if (t.os_ver) |os_ver| { |
| 3265 | std.log.info("zig can provide libc for related target {s}-{s}.{d}-{s}", .{ | 3265 | std.log.info("zig can provide libc for related target {s}-{s}.{d}-{s}", .{ |
| ... | @@ -3530,16 +3530,16 @@ fn createModule( | ... | @@ -3530,16 +3530,16 @@ fn createModule( |
| 3530 | } | 3530 | } |
| 3531 | } | 3531 | } |
| 3532 | 3532 | ||
| 3533 | const target_query = parseTargetQueryOrReportFatalError(arena, target_parse_options); | 3533 | const target_query = std.zig.parseTargetQueryOrReportFatalError(arena, target_parse_options); |
| 3534 | const adjusted_target_query = a: { | 3534 | const adjusted_target_query = a: { |
| 3535 | if (!target_query.isNative()) break :a target_query; | 3535 | if (!target_query.isNative()) break :a target_query; |
| 3536 | if (create_module.host_triple) |triple| target_parse_options.arch_os_abi = triple; | 3536 | if (create_module.host_triple) |triple| target_parse_options.arch_os_abi = triple; |
| 3537 | if (create_module.host_cpu) |cpu| target_parse_options.cpu_features = cpu; | 3537 | if (create_module.host_cpu) |cpu| target_parse_options.cpu_features = cpu; |
| 3538 | if (create_module.host_dynamic_linker) |dl| target_parse_options.dynamic_linker = dl; | 3538 | 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); |
| 3540 | }; | 3540 | }; |
| 3541 | 3541 | ||
| 3542 | const target = resolveTargetQueryOrFatal(adjusted_target_query); | 3542 | const target = std.zig.resolveTargetQueryOrFatal(adjusted_target_query); |
| 3543 | break :t .{ | 3543 | break :t .{ |
| 3544 | .result = target, | 3544 | .result = target, |
| 3545 | .is_native_os = target_query.isNativeOs(), | 3545 | .is_native_os = target_query.isNativeOs(), |
| ... | @@ -4210,59 +4210,6 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void { | ... | @@ -4210,59 +4210,6 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void { |
| 4210 | } | 4210 | } |
| 4211 | } | 4211 | } |
| 4212 | 4212 | ||
| 4213 | fn 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 | |||
| 4266 | fn runOrTest( | 4213 | fn runOrTest( |
| 4267 | comp: *Compilation, | 4214 | comp: *Compilation, |
| 4268 | gpa: Allocator, | 4215 | gpa: Allocator, |
| ... | @@ -4871,9 +4818,9 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes: | ... | @@ -4871,9 +4818,9 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes: |
| 4871 | .os_tag = .windows, | 4818 | .os_tag = .windows, |
| 4872 | .abi = .msvc, | 4819 | .abi = .msvc, |
| 4873 | }; | 4820 | }; |
| 4874 | const target = resolveTargetQueryOrFatal(target_query); | 4821 | const target = std.zig.resolveTargetQueryOrFatal(target_query); |
| 4875 | const is_native_abi = target_query.isNativeAbi(); | 4822 | 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| { |
| 4877 | if (cur_includes == .any) { | 4824 | if (cur_includes == .any) { |
| 4878 | // fall back to mingw | 4825 | // fall back to mingw |
| 4879 | cur_includes = .gnu; | 4826 | cur_includes = .gnu; |
| ... | @@ -4899,9 +4846,9 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes: | ... | @@ -4899,9 +4846,9 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes: |
| 4899 | .os_tag = .windows, | 4846 | .os_tag = .windows, |
| 4900 | .abi = .gnu, | 4847 | .abi = .gnu, |
| 4901 | }; | 4848 | }; |
| 4902 | const target = resolveTargetQueryOrFatal(target_query); | 4849 | const target = std.zig.resolveTargetQueryOrFatal(target_query); |
| 4903 | const is_native_abi = target_query.isNativeAbi(); | 4850 | 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); |
| 4905 | return .{ | 4852 | return .{ |
| 4906 | .include_paths = detected_libc.libc_include_dir_list, | 4853 | .include_paths = detected_libc.libc_include_dir_list, |
| 4907 | .target_abi = "gnu", | 4854 | .target_abi = "gnu", |
| ... | @@ -4912,136 +4859,6 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes: | ... | @@ -4912,136 +4859,6 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes: |
| 4912 | } | 4859 | } |
| 4913 | } | 4860 | } |
| 4914 | 4861 | ||
| 4915 | const 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 | |||
| 4934 | fn 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 | |||
| 5045 | const usage_init = | 4862 | const usage_init = |
| 5046 | \\Usage: zig init | 4863 | \\Usage: zig init |
| 5047 | \\ | 4864 | \\ |
| ... | @@ -5293,7 +5110,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { | ... | @@ -5293,7 +5110,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 5293 | 5110 | ||
| 5294 | const target_query: std.Target.Query = .{}; | 5111 | const target_query: std.Target.Query = .{}; |
| 5295 | const resolved_target: Package.Module.ResolvedTarget = .{ | 5112 | const resolved_target: Package.Module.ResolvedTarget = .{ |
| 5296 | .result = resolveTargetQueryOrFatal(target_query), | 5113 | .result = std.zig.resolveTargetQueryOrFatal(target_query), |
| 5297 | .is_native_os = true, | 5114 | .is_native_os = true, |
| 5298 | .is_native_abi = true, | 5115 | .is_native_abi = true, |
| 5299 | }; | 5116 | }; |
| ... | @@ -5711,12 +5528,13 @@ fn jitCmd( | ... | @@ -5711,12 +5528,13 @@ fn jitCmd( |
| 5711 | args: []const []const u8, | 5528 | args: []const []const u8, |
| 5712 | cmd_name: []const u8, | 5529 | cmd_name: []const u8, |
| 5713 | root_src_path: []const u8, | 5530 | root_src_path: []const u8, |
| 5531 | prepend_zig_lib_dir_path: bool, | ||
| 5714 | ) !void { | 5532 | ) !void { |
| 5715 | const color: Color = .auto; | 5533 | const color: Color = .auto; |
| 5716 | 5534 | ||
| 5717 | const target_query: std.Target.Query = .{}; | 5535 | const target_query: std.Target.Query = .{}; |
| 5718 | const resolved_target: Package.Module.ResolvedTarget = .{ | 5536 | const resolved_target: Package.Module.ResolvedTarget = .{ |
| 5719 | .result = resolveTargetQueryOrFatal(target_query), | 5537 | .result = std.zig.resolveTargetQueryOrFatal(target_query), |
| 5720 | .is_native_os = true, | 5538 | .is_native_os = true, |
| 5721 | .is_native_abi = true, | 5539 | .is_native_abi = true, |
| 5722 | }; | 5540 | }; |
| ... | @@ -5766,7 +5584,7 @@ fn jitCmd( | ... | @@ -5766,7 +5584,7 @@ fn jitCmd( |
| 5766 | defer thread_pool.deinit(); | 5584 | defer thread_pool.deinit(); |
| 5767 | 5585 | ||
| 5768 | var child_argv: std.ArrayListUnmanaged([]const u8) = .{}; | 5586 | var child_argv: std.ArrayListUnmanaged([]const u8) = .{}; |
| 5769 | try child_argv.ensureUnusedCapacity(arena, args.len + 1); | 5587 | try child_argv.ensureUnusedCapacity(arena, args.len + 2); |
| 5770 | 5588 | ||
| 5771 | // We want to release all the locks before executing the child process, so we make a nice | 5589 | // We want to release all the locks before executing the child process, so we make a nice |
| 5772 | // big block here to ensure the cleanup gets run when we extract out our argv. | 5590 | // big block here to ensure the cleanup gets run when we extract out our argv. |
| ... | @@ -5829,6 +5647,9 @@ fn jitCmd( | ... | @@ -5829,6 +5647,9 @@ fn jitCmd( |
| 5829 | child_argv.appendAssumeCapacity(exe_path); | 5647 | child_argv.appendAssumeCapacity(exe_path); |
| 5830 | } | 5648 | } |
| 5831 | 5649 | ||
| 5650 | if (prepend_zig_lib_dir_path) | ||
| 5651 | child_argv.appendAssumeCapacity(zig_lib_directory.path.?); | ||
| 5652 | |||
| 5832 | child_argv.appendSliceAssumeCapacity(args); | 5653 | child_argv.appendSliceAssumeCapacity(args); |
| 5833 | 5654 | ||
| 5834 | if (process.can_execv) { | 5655 | if (process.can_execv) { |
| ... | @@ -6703,7 +6524,7 @@ fn warnAboutForeignBinaries( | ... | @@ -6703,7 +6524,7 @@ fn warnAboutForeignBinaries( |
| 6703 | link_libc: bool, | 6524 | link_libc: bool, |
| 6704 | ) !void { | 6525 | ) !void { |
| 6705 | const host_query: std.Target.Query = .{}; | 6526 | const host_query: std.Target.Query = .{}; |
| 6706 | const host_target = resolveTargetQueryOrFatal(host_query); | 6527 | const host_target = std.zig.resolveTargetQueryOrFatal(host_query); |
| 6707 | 6528 | ||
| 6708 | switch (std.zig.system.getExternalExecutor(host_target, target, .{ .link_libc = link_libc })) { | 6529 | switch (std.zig.system.getExternalExecutor(host_target, target, .{ .link_libc = link_libc })) { |
| 6709 | .native => return, | 6530 | .native => return, |
| ... | @@ -7559,11 +7380,6 @@ fn parseWasiExecModel(s: []const u8) std.builtin.WasiExecModel { | ... | @@ -7559,11 +7380,6 @@ fn parseWasiExecModel(s: []const u8) std.builtin.WasiExecModel { |
| 7559 | fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{s}); | 7380 | fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{s}); |
| 7560 | } | 7381 | } |
| 7561 | 7382 | ||
| 7562 | fn 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 | |||
| 7567 | fn parseStackSize(s: []const u8) u64 { | 7383 | fn parseStackSize(s: []const u8) u64 { |
| 7568 | return std.fmt.parseUnsigned(u64, s, 0) catch |err| | 7384 | return std.fmt.parseUnsigned(u64, s, 0) catch |err| |
| 7569 | fatal("unable to parse stack size '{s}': {s}", .{ s, @errorName(err) }); | 7385 | fatal("unable to parse stack size '{s}': {s}", .{ s, @errorName(err) }); |
src/musl.zig+2-25| ... | @@ -4,6 +4,7 @@ const mem = std.mem; | ... | @@ -4,6 +4,7 @@ const mem = std.mem; |
| 4 | const path = std.fs.path; | 4 | const path = std.fs.path; |
| 5 | const assert = std.debug.assert; | 5 | const assert = std.debug.assert; |
| 6 | const Module = @import("Package/Module.zig"); | 6 | const Module = @import("Package/Module.zig"); |
| 7 | const archName = std.zig.target.muslArchName; | ||
| 7 | 8 | ||
| 8 | const Compilation = @import("Compilation.zig"); | 9 | const Compilation = @import("Compilation.zig"); |
| 9 | const build_options = @import("build_options"); | 10 | const build_options = @import("build_options"); |
| ... | @@ -294,30 +295,6 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr | ... | @@ -294,30 +295,6 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr |
| 294 | } | 295 | } |
| 295 | } | 296 | } |
| 296 | 297 | ||
| 297 | fn 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 | |||
| 314 | pub 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 | |||
| 321 | // Return true if musl has arch-specific crti/crtn sources. | 298 | // Return true if musl has arch-specific crti/crtn sources. |
| 322 | // See lib/libc/musl/crt/ARCH/crt?.s . | 299 | // See lib/libc/musl/crt/ARCH/crt?.s . |
| 323 | pub fn needsCrtiCrtn(target: std.Target) bool { | 300 | pub fn needsCrtiCrtn(target: std.Target) bool { |
| ... | @@ -405,7 +382,7 @@ fn addCcArgs( | ... | @@ -405,7 +382,7 @@ fn addCcArgs( |
| 405 | const arch_name = archName(target.cpu.arch); | 382 | const arch_name = archName(target.cpu.arch); |
| 406 | const os_name = @tagName(target.os.tag); | 383 | const os_name = @tagName(target.os.tag); |
| 407 | const triple = try std.fmt.allocPrint(arena, "{s}-{s}-musl", .{ | 384 | 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, |
| 409 | }); | 386 | }); |
| 410 | const o_arg = if (want_O3) "-O3" else "-Os"; | 387 | const o_arg = if (want_O3) "-O3" else "-Os"; |
| 411 | 388 |
src/print_env.zig+2-2| ... | @@ -47,9 +47,9 @@ pub fn cmdEnv(arena: Allocator, args: []const []const u8, stdout: std.fs.File.Wr | ... | @@ -47,9 +47,9 @@ pub fn cmdEnv(arena: Allocator, args: []const []const u8, stdout: std.fs.File.Wr |
| 47 | 47 | ||
| 48 | try jws.objectField("env"); | 48 | try jws.objectField("env"); |
| 49 | try jws.beginObject(); | 49 | try jws.beginObject(); |
| 50 | inline for (@typeInfo(introspect.EnvVar).Enum.fields) |field| { | 50 | inline for (@typeInfo(std.zig.EnvVar).Enum.fields) |field| { |
| 51 | try jws.objectField(field.name); | 51 | 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)); |
| 53 | } | 53 | } |
| 54 | try jws.endObject(); | 54 | try jws.endObject(); |
| 55 | 55 |
src/print_targets.zig+1-1| ... | @@ -67,7 +67,7 @@ pub fn cmdTargets( | ... | @@ -67,7 +67,7 @@ pub fn cmdTargets( |
| 67 | 67 | ||
| 68 | try jws.objectField("libc"); | 68 | try jws.objectField("libc"); |
| 69 | try jws.beginArray(); | 69 | try jws.beginArray(); |
| 70 | for (target.available_libcs) |libc| { | 70 | for (std.zig.target.available_libcs) |libc| { |
| 71 | const tmp = try std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ | 71 | const tmp = try std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ |
| 72 | @tagName(libc.arch), @tagName(libc.os), @tagName(libc.abi), | 72 | @tagName(libc.arch), @tagName(libc.os), @tagName(libc.abi), |
| 73 | }); | 73 | }); |
src/target.zig-163| ... | @@ -6,169 +6,6 @@ const Feature = @import("Module.zig").Feature; | ... | @@ -6,169 +6,6 @@ const Feature = @import("Module.zig").Feature; |
| 6 | 6 | ||
| 7 | pub const default_stack_protector_buffer_size = 4; | 7 | pub const default_stack_protector_buffer_size = 4; |
| 8 | 8 | ||
| 9 | pub 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 | |||
| 19 | pub 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 | |||
| 82 | pub 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 | |||
| 137 | pub 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 | |||
| 153 | pub 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 | |||
| 172 | pub fn cannotDynamicLink(target: std.Target) bool { | 9 | pub fn cannotDynamicLink(target: std.Target) bool { |
| 173 | return switch (target.os.tag) { | 10 | return switch (target.os.tag) { |
| 174 | .freestanding, .other => true, | 11 | .freestanding, .other => true, |
src/wasi_libc.zig+1-3| ... | @@ -5,8 +5,6 @@ const path = std.fs.path; | ... | @@ -5,8 +5,6 @@ const path = std.fs.path; |
| 5 | const Allocator = std.mem.Allocator; | 5 | const Allocator = std.mem.Allocator; |
| 6 | const Compilation = @import("Compilation.zig"); | 6 | const Compilation = @import("Compilation.zig"); |
| 7 | const build_options = @import("build_options"); | 7 | const build_options = @import("build_options"); |
| 8 | const target_util = @import("target.zig"); | ||
| 9 | const musl = @import("musl.zig"); | ||
| 10 | 8 | ||
| 11 | pub const CRTFile = enum { | 9 | pub const CRTFile = enum { |
| 12 | crt1_reactor_o, | 10 | crt1_reactor_o, |
| ... | @@ -273,7 +271,7 @@ fn addCCArgs( | ... | @@ -273,7 +271,7 @@ fn addCCArgs( |
| 273 | options: CCOptions, | 271 | options: CCOptions, |
| 274 | ) error{OutOfMemory}!void { | 272 | ) error{OutOfMemory}!void { |
| 275 | const target = comp.getTarget(); | 273 | const target = comp.getTarget(); |
| 276 | const arch_name = musl.archNameHeaders(target.cpu.arch); | 274 | const arch_name = std.zig.target.muslArchNameHeaders(target.cpu.arch); |
| 277 | const os_name = @tagName(target.os.tag); | 275 | const os_name = @tagName(target.os.tag); |
| 278 | const triple = try std.fmt.allocPrint(arena, "{s}-{s}-musl", .{ arch_name, os_name }); | 276 | const triple = try std.fmt.allocPrint(arena, "{s}-{s}-musl", .{ arch_name, os_name }); |
| 279 | const o_arg = if (options.want_O3) "-O3" else "-Os"; | 277 | const o_arg = if (options.want_O3) "-O3" else "-Os"; |
src/windows_sdk.zig deleted-965| ... | @@ -1,965 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | |||
| 4 | const windows = std.os.windows; | ||
| 5 | const RRF = windows.advapi32.RRF; | ||
| 6 | |||
| 7 | const WINDOWS_KIT_REG_KEY = "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots"; | ||
| 8 | |||
| 9 | // https://learn.microsoft.com/en-us/windows/win32/msi/productversion | ||
| 10 | const 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 | ||
| 12 | const 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. | ||
| 17 | fn 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 | |||
| 87 | const 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 | |||
| 178 | const 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 | |||
| 344 | pub 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 | |||
| 440 | pub 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 | |||
| 511 | pub 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 | |||
| 580 | const 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 | }; | ||