struct LibCInstallation [src]

Alias for std.zig.LibCInstallation

See the render function implementation for documentation of the fields.

Fields

include_dir: ?[]const u8 = null
sys_include_dir: ?[]const u8 = null
crt_dir: ?[]const u8 = null
msvc_lib_dir: ?[]const u8 = null
kernel32_lib_dir: ?[]const u8 = null
gcc_dir: ?[]const u8 = null

Members

Source

//! See the render function implementation for documentation of the fields. include_dir: ?[]const u8 = null, sys_include_dir: ?[]const u8 = null, crt_dir: ?[]const u8 = null, msvc_lib_dir: ?[]const u8 = null, kernel32_lib_dir: ?[]const u8 = null, gcc_dir: ?[]const u8 = null, pub const FindError = error{ OutOfMemory, FileSystem, UnableToSpawnCCompiler, CCompilerExitCode, CCompilerCrashed, CCompilerCannotFindHeaders, LibCRuntimeNotFound, LibCStdLibHeaderNotFound, LibCKernel32LibNotFound, UnsupportedArchitecture, WindowsSdkNotFound, DarwinSdkNotFound, ZigIsTheCCompiler, }; pub fn parse( allocator: Allocator, libc_file: []const u8, target: std.Target, ) !LibCInstallation { var self: LibCInstallation = .{}; const fields = std.meta.fields(LibCInstallation); const FoundKey = struct { found: bool, allocated: ?[:0]u8, }; var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** fields.len; errdefer { self = .{}; for (found_keys) |found_key| { if (found_key.allocated) |s| allocator.free(s); } } const contents = try std.fs.cwd().readFileAlloc(allocator, libc_file, std.math.maxInt(usize)); defer allocator.free(contents); var it = std.mem.tokenizeScalar(u8, contents, '\n'); while (it.next()) |line| { if (line.len == 0 or line[0] == '#') continue; var line_it = std.mem.splitScalar(u8, line, '='); const name = line_it.first(); const value = line_it.rest(); inline for (fields, 0..) |field, i| { if (std.mem.eql(u8, name, field.name)) { found_keys[i].found = true; if (value.len == 0) { @field(self, field.name) = null; } else { found_keys[i].allocated = try allocator.dupeZ(u8, value); @field(self, field.name) = found_keys[i].allocated; } break; } } } inline for (fields, 0..) |field, i| { if (!found_keys[i].found) { log.err("missing field: {s}", .{field.name}); return error.ParseError; } } if (self.include_dir == null) { log.err("include_dir may not be empty", .{}); return error.ParseError; } if (self.sys_include_dir == null) { log.err("sys_include_dir may not be empty", .{}); return error.ParseError; } const os_tag = target.os.tag; if (self.crt_dir == null and !target.os.tag.isDarwin()) { log.err("crt_dir may not be empty for {s}", .{@tagName(os_tag)}); return error.ParseError; } if (self.msvc_lib_dir == null and os_tag == .windows and (target.abi == .msvc or target.abi == .itanium)) { log.err("msvc_lib_dir may not be empty for {s}-{s}", .{ @tagName(os_tag), @tagName(target.abi), }); return error.ParseError; } if (self.kernel32_lib_dir == null and os_tag == .windows and (target.abi == .msvc or target.abi == .itanium)) { log.err("kernel32_lib_dir may not be empty for {s}-{s}", .{ @tagName(os_tag), @tagName(target.abi), }); return error.ParseError; } if (self.gcc_dir == null and os_tag == .haiku) { log.err("gcc_dir may not be empty for {s}", .{@tagName(os_tag)}); return error.ParseError; } return self; } pub fn render(self: LibCInstallation, out: anytype) !void { @setEvalBranchQuota(4000); const include_dir = self.include_dir orelse ""; const sys_include_dir = self.sys_include_dir orelse ""; const crt_dir = self.crt_dir orelse ""; const msvc_lib_dir = self.msvc_lib_dir orelse ""; const kernel32_lib_dir = self.kernel32_lib_dir orelse ""; const gcc_dir = self.gcc_dir orelse ""; try out.print( \\# The directory that contains `stdlib.h`. \\# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null` \\include_dir={s} \\ \\# The system-specific include directory. May be the same as `include_dir`. \\# On Windows it's the directory that includes `vcruntime.h`. \\# On POSIX it's the directory that includes `sys/errno.h`. \\sys_include_dir={s} \\ \\# The directory that contains `crt1.o` or `crt2.o`. \\# On POSIX, can be found with `cc -print-file-name=crt1.o`. \\# Not needed when targeting MacOS. \\crt_dir={s} \\ \\# The directory that contains `vcruntime.lib`. \\# Only needed when targeting MSVC on Windows. \\msvc_lib_dir={s} \\ \\# The directory that contains `kernel32.lib`. \\# Only needed when targeting MSVC on Windows. \\kernel32_lib_dir={s} \\ \\# The directory that contains `crtbeginS.o` and `crtendS.o` \\# Only needed when targeting Haiku. \\gcc_dir={s} \\ , .{ include_dir, sys_include_dir, crt_dir, msvc_lib_dir, kernel32_lib_dir, gcc_dir, }); } pub const FindNativeOptions = struct { allocator: Allocator, target: std.Target, /// If enabled, will print human-friendly errors to stderr. verbose: bool = false, }; /// Finds the default, native libc. pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation { var self: LibCInstallation = .{}; if (is_darwin and args.target.os.tag.isDarwin()) { if (!std.zig.system.darwin.isSdkInstalled(args.allocator)) return error.DarwinSdkNotFound; const sdk = std.zig.system.darwin.getSdk(args.allocator, args.target) orelse return error.DarwinSdkNotFound; defer args.allocator.free(sdk); self.include_dir = try fs.path.join(args.allocator, &.{ sdk, "usr/include", }); self.sys_include_dir = try fs.path.join(args.allocator, &.{ sdk, "usr/include", }); return self; } else if (is_windows) { const sdk = std.zig.WindowsSdk.find(args.allocator, args.target.cpu.arch) catch |err| switch (err) { error.NotFound => return error.WindowsSdkNotFound, error.PathTooLong => return error.WindowsSdkNotFound, error.OutOfMemory => return error.OutOfMemory, }; defer sdk.free(args.allocator); try self.findNativeMsvcIncludeDir(args, sdk); try self.findNativeMsvcLibDir(args, sdk); try self.findNativeKernel32LibDir(args, sdk); try self.findNativeIncludeDirWindows(args, sdk); try self.findNativeCrtDirWindows(args, sdk); } else if (is_haiku) { try self.findNativeIncludeDirPosix(args); try self.findNativeGccDirHaiku(args); self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib"); } else if (builtin.target.os.tag.isSolarish()) { // There is only one libc, and its headers/libraries are always in the same spot. self.include_dir = try args.allocator.dupeZ(u8, "/usr/include"); self.sys_include_dir = try args.allocator.dupeZ(u8, "/usr/include"); self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib/64"); } else if (std.process.can_spawn) { try self.findNativeIncludeDirPosix(args); switch (builtin.target.os.tag) { .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib"), .linux => try self.findNativeCrtDirPosix(args), else => {}, } } else { return error.LibCRuntimeNotFound; } return self; } /// Must be the same allocator passed to `parse` or `findNative`. pub fn deinit(self: *LibCInstallation, allocator: Allocator) void { const fields = std.meta.fields(LibCInstallation); inline for (fields) |field| { if (@field(self, field.name)) |payload| { allocator.free(payload); } } self.* = undefined; } fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void { const allocator = args.allocator; // Detect infinite loops. var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) { error.Unexpected => unreachable, // WASI-only else => |e| return e, }; defer env_map.deinit(); const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: { if (std.mem.eql(u8, phase, "1")) { try env_map.put(inf_loop_env_key, "2"); break :blk true; } else { return error.ZigIsTheCCompiler; } } else blk: { try env_map.put(inf_loop_env_key, "1"); break :blk false; }; const dev_null = if (is_windows) "nul" else "/dev/null"; var argv = std.ArrayList([]const u8).init(allocator); defer argv.deinit(); try appendCcExe(&argv, skip_cc_env_var); try argv.appendSlice(&.{ "-E", "-Wp,-v", "-xc", dev_null, }); const run_res = std.process.Child.run(.{ .allocator = allocator, .argv = argv.items, .max_output_bytes = 1024 * 1024, .env_map = &env_map, // Some C compilers, such as Clang, are known to rely on argv[0] to find the path // to their own executable, without even bothering to resolve PATH. This results in the message: // error: unable to execute command: Executable "" doesn't exist! // So we use the expandArg0 variant of ChildProcess to give them a helping hand. .expand_arg0 = .expand, }) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => { printVerboseInvocation(argv.items, null, args.verbose, null); return error.UnableToSpawnCCompiler; }, }; defer { allocator.free(run_res.stdout); allocator.free(run_res.stderr); } switch (run_res.term) { .Exited => |code| if (code != 0) { printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr); return error.CCompilerExitCode; }, else => { printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr); return error.CCompilerCrashed; }, } var it = std.mem.tokenizeAny(u8, run_res.stderr, "\n\r"); var search_paths = std.ArrayList([]const u8).init(allocator); defer search_paths.deinit(); while (it.next()) |line| { if (line.len != 0 and line[0] == ' ') { try search_paths.append(line); } } if (search_paths.items.len == 0) { return error.CCompilerCannotFindHeaders; } const include_dir_example_file = if (is_haiku) "posix/stdlib.h" else "stdlib.h"; const sys_include_dir_example_file = if (is_windows) "sys\\types.h" else if (is_haiku) "errno.h" else "sys/errno.h"; var path_i: usize = 0; while (path_i < search_paths.items.len) : (path_i += 1) { // search in reverse order const search_path_untrimmed = search_paths.items[search_paths.items.len - path_i - 1]; const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " "); var search_dir = fs.cwd().openDir(search_path, .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.NoDevice, => continue, else => return error.FileSystem, }; defer search_dir.close(); if (self.include_dir == null) { if (search_dir.accessZ(include_dir_example_file, .{})) |_| { self.include_dir = try allocator.dupeZ(u8, search_path); } else |err| switch (err) { error.FileNotFound => {}, else => return error.FileSystem, } } if (self.sys_include_dir == null) { if (search_dir.accessZ(sys_include_dir_example_file, .{})) |_| { self.sys_include_dir = try allocator.dupeZ(u8, search_path); } else |err| switch (err) { error.FileNotFound => {}, else => return error.FileSystem, } } if (self.include_dir != null and self.sys_include_dir != null) { // Success. return; } } return error.LibCStdLibHeaderNotFound; } fn findNativeIncludeDirWindows( self: *LibCInstallation, args: FindNativeOptions, sdk: std.zig.WindowsSdk, ) FindError!void { const allocator = args.allocator; var install_buf: [2]std.zig.WindowsSdk.Installation = undefined; const installs = fillInstallations(&install_buf, sdk); var result_buf = std.ArrayList(u8).init(allocator); defer result_buf.deinit(); for (installs) |install| { result_buf.shrinkAndFree(0); try result_buf.writer().print("{s}\\Include\\{s}\\ucrt", .{ install.path, install.version }); var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.NoDevice, => continue, else => return error.FileSystem, }; defer dir.close(); dir.accessZ("stdlib.h", .{}) catch |err| switch (err) { error.FileNotFound => continue, else => return error.FileSystem, }; self.include_dir = try result_buf.toOwnedSlice(); return; } return error.LibCStdLibHeaderNotFound; } fn findNativeCrtDirWindows( self: *LibCInstallation, args: FindNativeOptions, sdk: std.zig.WindowsSdk, ) FindError!void { const allocator = args.allocator; var install_buf: [2]std.zig.WindowsSdk.Installation = undefined; const installs = fillInstallations(&install_buf, sdk); var result_buf = std.ArrayList(u8).init(allocator); defer result_buf.deinit(); const arch_sub_dir = switch (args.target.cpu.arch) { .x86 => "x86", .x86_64 => "x64", .arm, .armeb => "arm", .aarch64 => "arm64", else => return error.UnsupportedArchitecture, }; for (installs) |install| { result_buf.shrinkAndFree(0); try result_buf.writer().print("{s}\\Lib\\{s}\\ucrt\\{s}", .{ install.path, install.version, arch_sub_dir }); var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.NoDevice, => continue, else => return error.FileSystem, }; defer dir.close(); dir.accessZ("ucrt.lib", .{}) catch |err| switch (err) { error.FileNotFound => continue, else => return error.FileSystem, }; self.crt_dir = try result_buf.toOwnedSlice(); return; } return error.LibCRuntimeNotFound; } fn findNativeCrtDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void { self.crt_dir = try ccPrintFileName(.{ .allocator = args.allocator, .search_basename = switch (args.target.os.tag) { .linux => if (args.target.abi.isAndroid()) "crtbegin_dynamic.o" else "crt1.o", else => "crt1.o", }, .want_dirname = .only_dir, .verbose = args.verbose, }); } fn findNativeGccDirHaiku(self: *LibCInstallation, args: FindNativeOptions) FindError!void { self.gcc_dir = try ccPrintFileName(.{ .allocator = args.allocator, .search_basename = "crtbeginS.o", .want_dirname = .only_dir, .verbose = args.verbose, }); } fn findNativeKernel32LibDir( self: *LibCInstallation, args: FindNativeOptions, sdk: std.zig.WindowsSdk, ) FindError!void { const allocator = args.allocator; var install_buf: [2]std.zig.WindowsSdk.Installation = undefined; const installs = fillInstallations(&install_buf, sdk); var result_buf = std.ArrayList(u8).init(allocator); defer result_buf.deinit(); const arch_sub_dir = switch (args.target.cpu.arch) { .x86 => "x86", .x86_64 => "x64", .arm, .armeb => "arm", .aarch64 => "arm64", else => return error.UnsupportedArchitecture, }; for (installs) |install| { result_buf.shrinkAndFree(0); const stream = result_buf.writer(); try stream.print("{s}\\Lib\\{s}\\um\\{s}", .{ install.path, install.version, arch_sub_dir }); var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.NoDevice, => continue, else => return error.FileSystem, }; defer dir.close(); dir.accessZ("kernel32.lib", .{}) catch |err| switch (err) { error.FileNotFound => continue, else => return error.FileSystem, }; self.kernel32_lib_dir = try result_buf.toOwnedSlice(); return; } return error.LibCKernel32LibNotFound; } fn findNativeMsvcIncludeDir( self: *LibCInstallation, args: FindNativeOptions, sdk: std.zig.WindowsSdk, ) FindError!void { const allocator = args.allocator; const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCStdLibHeaderNotFound; const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound; const up2 = fs.path.dirname(up1) orelse return error.LibCStdLibHeaderNotFound; const dir_path = try fs.path.join(allocator, &[_][]const u8{ up2, "include" }); errdefer allocator.free(dir_path); var dir = fs.cwd().openDir(dir_path, .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.NoDevice, => return error.LibCStdLibHeaderNotFound, else => return error.FileSystem, }; defer dir.close(); dir.accessZ("vcruntime.h", .{}) catch |err| switch (err) { error.FileNotFound => return error.LibCStdLibHeaderNotFound, else => return error.FileSystem, }; self.sys_include_dir = dir_path; } fn findNativeMsvcLibDir( self: *LibCInstallation, args: FindNativeOptions, sdk: std.zig.WindowsSdk, ) FindError!void { const allocator = args.allocator; const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCRuntimeNotFound; self.msvc_lib_dir = try allocator.dupe(u8, msvc_lib_dir); } pub const CCPrintFileNameOptions = struct { allocator: Allocator, search_basename: []const u8, want_dirname: enum { full_path, only_dir }, verbose: bool = false, }; /// caller owns returned memory fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 { const allocator = args.allocator; // Detect infinite loops. var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) { error.Unexpected => unreachable, // WASI-only else => |e| return e, }; defer env_map.deinit(); const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: { if (std.mem.eql(u8, phase, "1")) { try env_map.put(inf_loop_env_key, "2"); break :blk true; } else { return error.ZigIsTheCCompiler; } } else blk: { try env_map.put(inf_loop_env_key, "1"); break :blk false; }; var argv = std.ArrayList([]const u8).init(allocator); defer argv.deinit(); const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={s}", .{args.search_basename}); defer allocator.free(arg1); try appendCcExe(&argv, skip_cc_env_var); try argv.append(arg1); const run_res = std.process.Child.run(.{ .allocator = allocator, .argv = argv.items, .max_output_bytes = 1024 * 1024, .env_map = &env_map, // Some C compilers, such as Clang, are known to rely on argv[0] to find the path // to their own executable, without even bothering to resolve PATH. This results in the message: // error: unable to execute command: Executable "" doesn't exist! // So we use the expandArg0 variant of ChildProcess to give them a helping hand. .expand_arg0 = .expand, }) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => return error.UnableToSpawnCCompiler, }; defer { allocator.free(run_res.stdout); allocator.free(run_res.stderr); } switch (run_res.term) { .Exited => |code| if (code != 0) { printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr); return error.CCompilerExitCode; }, else => { printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr); return error.CCompilerCrashed; }, } var it = std.mem.tokenizeAny(u8, run_res.stdout, "\n\r"); const line = it.next() orelse return error.LibCRuntimeNotFound; // When this command fails, it returns exit code 0 and duplicates the input file name. // So we detect failure by checking if the output matches exactly the input. if (std.mem.eql(u8, line, args.search_basename)) return error.LibCRuntimeNotFound; switch (args.want_dirname) { .full_path => return allocator.dupeZ(u8, line), .only_dir => { const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound; return allocator.dupeZ(u8, dirname); }, } } fn printVerboseInvocation( argv: []const []const u8, search_basename: ?[]const u8, verbose: bool, stderr: ?[]const u8, ) void { if (!verbose) return; if (search_basename) |s| { std.debug.print("Zig attempted to find the file '{s}' by executing this command:\n", .{s}); } else { std.debug.print("Zig attempted to find the path to native system libc headers by executing this command:\n", .{}); } for (argv, 0..) |arg, i| { if (i != 0) std.debug.print(" ", .{}); std.debug.print("{s}", .{arg}); } std.debug.print("\n", .{}); if (stderr) |s| { std.debug.print("Output:\n==========\n{s}\n==========\n", .{s}); } } fn fillInstallations( installs: *[2]std.zig.WindowsSdk.Installation, sdk: std.zig.WindowsSdk, ) []std.zig.WindowsSdk.Installation { var installs_len: usize = 0; if (sdk.windows10sdk) |windows10sdk| { installs[installs_len] = windows10sdk; installs_len += 1; } if (sdk.windows81sdk) |windows81sdk| { installs[installs_len] = windows81sdk; installs_len += 1; } return installs[0..installs_len]; } const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS"; fn appendCcExe(args: *std.ArrayList([]const u8), skip_cc_env_var: bool) !void { const default_cc_exe = if (is_windows) "cc.exe" else "cc"; try args.ensureUnusedCapacity(1); if (skip_cc_env_var) { args.appendAssumeCapacity(default_cc_exe); return; } const cc_env_var = std.zig.EnvVar.CC.getPosix() orelse { args.appendAssumeCapacity(default_cc_exe); return; }; // Respect space-separated flags to the C compiler. var it = std.mem.tokenizeScalar(u8, cc_env_var, ' '); while (it.next()) |arg| { try args.append(arg); } } /// These are basenames. This data is produced with a pure function. See also /// `CsuPaths`. pub const CrtBasenames = struct { crt0: ?[]const u8 = null, crti: ?[]const u8 = null, crtbegin: ?[]const u8 = null, crtend: ?[]const u8 = null, crtn: ?[]const u8 = null, pub const GetArgs = struct { target: std.Target, link_libc: bool, output_mode: std.builtin.OutputMode, link_mode: std.builtin.LinkMode, pie: bool, }; /// Determine file system path names of C runtime startup objects for supported /// link modes. pub fn get(args: GetArgs) CrtBasenames { // crt objects are only required for libc. if (!args.link_libc) return .{}; // Flatten crt cases. const mode: enum { dynamic_lib, dynamic_exe, dynamic_pie, static_exe, static_pie, } = switch (args.output_mode) { .Obj => return .{}, .Lib => switch (args.link_mode) { .dynamic => .dynamic_lib, .static => return .{}, }, .Exe => switch (args.link_mode) { .dynamic => if (args.pie) .dynamic_pie else .dynamic_exe, .static => if (args.pie) .static_pie else .static_exe, }, }; const target = args.target; if (target.abi.isAndroid()) return switch (mode) { .dynamic_lib => .{ .crtbegin = "crtbegin_so.o", .crtend = "crtend_so.o", }, .dynamic_exe, .dynamic_pie => .{ .crtbegin = "crtbegin_dynamic.o", .crtend = "crtend_android.o", }, .static_exe, .static_pie => .{ .crtbegin = "crtbegin_static.o", .crtend = "crtend_android.o", }, }; return switch (target.os.tag) { .linux => switch (mode) { .dynamic_lib => .{ .crti = "crti.o", .crtn = "crtn.o", }, .dynamic_exe => .{ .crt0 = "crt1.o", .crti = "crti.o", .crtn = "crtn.o", }, .dynamic_pie => .{ .crt0 = "Scrt1.o", .crti = "crti.o", .crtn = "crtn.o", }, .static_exe => .{ .crt0 = "crt1.o", .crti = "crti.o", .crtn = "crtn.o", }, .static_pie => .{ .crt0 = "rcrt1.o", .crti = "crti.o", .crtn = "crtn.o", }, }, .dragonfly => switch (mode) { .dynamic_lib => .{ .crti = "crti.o", .crtbegin = "crtbeginS.o", .crtend = "crtendS.o", .crtn = "crtn.o", }, .dynamic_exe => .{ .crt0 = "crt1.o", .crti = "crti.o", .crtbegin = "crtbegin.o", .crtend = "crtend.o", .crtn = "crtn.o", }, .dynamic_pie => .{ .crt0 = "Scrt1.o", .crti = "crti.o", .crtbegin = "crtbeginS.o", .crtend = "crtendS.o", .crtn = "crtn.o", }, .static_exe => .{ .crt0 = "crt1.o", .crti = "crti.o", .crtbegin = "crtbegin.o", .crtend = "crtend.o", .crtn = "crtn.o", }, .static_pie => .{ .crt0 = "Scrt1.o", .crti = "crti.o", .crtbegin = "crtbeginS.o", .crtend = "crtendS.o", .crtn = "crtn.o", }, }, .freebsd => switch (mode) { .dynamic_lib => .{ .crti = "crti.o", .crtbegin = "crtbeginS.o", .crtend = "crtendS.o", .crtn = "crtn.o", }, .dynamic_exe => .{ .crt0 = "crt1.o", .crti = "crti.o", .crtbegin = "crtbegin.o", .crtend = "crtend.o", .crtn = "crtn.o", }, .dynamic_pie => .{ .crt0 = "Scrt1.o", .crti = "crti.o", .crtbegin = "crtbeginS.o", .crtend = "crtendS.o", .crtn = "crtn.o", }, .static_exe => .{ .crt0 = "crt1.o", .crti = "crti.o", .crtbegin = "crtbeginT.o", .crtend = "crtend.o", .crtn = "crtn.o", }, .static_pie => .{ .crt0 = "Scrt1.o", .crti = "crti.o", .crtbegin = "crtbeginS.o", .crtend = "crtendS.o", .crtn = "crtn.o", }, }, .netbsd => switch (mode) { .dynamic_lib => .{ .crti = "crti.o", .crtbegin = "crtbeginS.o", .crtend = "crtendS.o", .crtn = "crtn.o", }, .dynamic_exe => .{ .crt0 = "crt0.o", .crti = "crti.o", .crtbegin = "crtbegin.o", .crtend = "crtend.o", .crtn = "crtn.o", }, .dynamic_pie => .{ .crt0 = "crt0.o", .crti = "crti.o", .crtbegin = "crtbeginS.o", .crtend = "crtendS.o", .crtn = "crtn.o", }, .static_exe => .{ .crt0 = "crt0.o", .crti = "crti.o", .crtbegin = "crtbeginT.o", .crtend = "crtend.o", .crtn = "crtn.o", }, .static_pie => .{ .crt0 = "crt0.o", .crti = "crti.o", .crtbegin = "crtbeginT.o", .crtend = "crtendS.o", .crtn = "crtn.o", }, }, .openbsd => switch (mode) { .dynamic_lib => .{ .crtbegin = "crtbeginS.o", .crtend = "crtendS.o", }, .dynamic_exe, .dynamic_pie => .{ .crt0 = "crt0.o", .crtbegin = "crtbegin.o", .crtend = "crtend.o", }, .static_exe, .static_pie => .{ .crt0 = "rcrt0.o", .crtbegin = "crtbegin.o", .crtend = "crtend.o", }, }, .haiku => switch (mode) { .dynamic_lib => .{ .crti = "crti.o", .crtbegin = "crtbeginS.o", .crtend = "crtendS.o", .crtn = "crtn.o", }, .dynamic_exe => .{ .crt0 = "start_dyn.o", .crti = "crti.o", .crtbegin = "crtbegin.o", .crtend = "crtend.o", .crtn = "crtn.o", }, .dynamic_pie => .{ .crt0 = "start_dyn.o", .crti = "crti.o", .crtbegin = "crtbeginS.o", .crtend = "crtendS.o", .crtn = "crtn.o", }, .static_exe => .{ .crt0 = "start_dyn.o", .crti = "crti.o", .crtbegin = "crtbegin.o", .crtend = "crtend.o", .crtn = "crtn.o", }, .static_pie => .{ .crt0 = "start_dyn.o", .crti = "crti.o", .crtbegin = "crtbeginS.o", .crtend = "crtendS.o", .crtn = "crtn.o", }, }, .solaris, .illumos => switch (mode) { .dynamic_lib => .{ .crti = "crti.o", .crtn = "crtn.o", }, .dynamic_exe, .dynamic_pie => .{ .crt0 = "crt1.o", .crti = "crti.o", .crtn = "crtn.o", }, .static_exe, .static_pie => .{}, }, else => .{}, }; } }; pub const CrtPaths = struct { crt0: ?Path = null, crti: ?Path = null, crtbegin: ?Path = null, crtend: ?Path = null, crtn: ?Path = null, }; pub fn resolveCrtPaths( lci: LibCInstallation, arena: Allocator, crt_basenames: CrtBasenames, target: std.Target, ) error{ OutOfMemory, LibCInstallationMissingCrtDir }!CrtPaths { const crt_dir_path: Path = .{ .root_dir = std.Build.Cache.Directory.cwd(), .sub_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir, }; switch (target.os.tag) { .dragonfly => { const gccv: []const u8 = if (target.os.version_range.semver.isAtLeast(.{ .major = 5, .minor = 4, .patch = 0, }) orelse true) "gcc80" else "gcc54"; return .{ .crt0 = if (crt_basenames.crt0) |basename| try crt_dir_path.join(arena, basename) else null, .crti = if (crt_basenames.crti) |basename| try crt_dir_path.join(arena, basename) else null, .crtbegin = if (crt_basenames.crtbegin) |basename| .{ .root_dir = crt_dir_path.root_dir, .sub_path = try fs.path.join(arena, &.{ crt_dir_path.sub_path, gccv, basename }), } else null, .crtend = if (crt_basenames.crtend) |basename| .{ .root_dir = crt_dir_path.root_dir, .sub_path = try fs.path.join(arena, &.{ crt_dir_path.sub_path, gccv, basename }), } else null, .crtn = if (crt_basenames.crtn) |basename| try crt_dir_path.join(arena, basename) else null, }; }, .haiku => { const gcc_dir_path: Path = .{ .root_dir = std.Build.Cache.Directory.cwd(), .sub_path = lci.gcc_dir orelse return error.LibCInstallationMissingCrtDir, }; return .{ .crt0 = if (crt_basenames.crt0) |basename| try crt_dir_path.join(arena, basename) else null, .crti = if (crt_basenames.crti) |basename| try crt_dir_path.join(arena, basename) else null, .crtbegin = if (crt_basenames.crtbegin) |basename| try gcc_dir_path.join(arena, basename) else null, .crtend = if (crt_basenames.crtend) |basename| try gcc_dir_path.join(arena, basename) else null, .crtn = if (crt_basenames.crtn) |basename| try crt_dir_path.join(arena, basename) else null, }; }, else => { return .{ .crt0 = if (crt_basenames.crt0) |basename| try crt_dir_path.join(arena, basename) else null, .crti = if (crt_basenames.crti) |basename| try crt_dir_path.join(arena, basename) else null, .crtbegin = if (crt_basenames.crtbegin) |basename| try crt_dir_path.join(arena, basename) else null, .crtend = if (crt_basenames.crtend) |basename| try crt_dir_path.join(arena, basename) else null, .crtn = if (crt_basenames.crtn) |basename| try crt_dir_path.join(arena, basename) else null, }; }, } } const LibCInstallation = @This(); const std = @import("std"); const builtin = @import("builtin"); const Target = std.Target; const fs = std.fs; const Allocator = std.mem.Allocator; const Path = std.Build.Cache.Path; const is_darwin = builtin.target.os.tag.isDarwin(); const is_windows = builtin.target.os.tag == .windows; const is_haiku = builtin.target.os.tag == .haiku; const log = std.log.scoped(.libc_installation);