zig/src-self-hosted/glibc.zig
Andrew Kelley 046dce9cef stage2: fix not creating cache o dir before writing to it
also remove non-working debug CLI options
2020-09-13 21:13:24 -07:00

652 lines
26 KiB
Zig

const std = @import("std");
const Allocator = std.mem.Allocator;
const target_util = @import("target.zig");
const mem = std.mem;
const Compilation = @import("Compilation.zig");
const path = std.fs.path;
const build_options = @import("build_options");
const trace = @import("tracy.zig").trace;
pub const Lib = struct {
name: []const u8,
sover: u8,
};
pub const Fn = struct {
name: []const u8,
lib: *const Lib,
};
pub const VerList = struct {
/// 7 is just the max number, we know statically it's big enough.
versions: [7]u8,
len: u8,
};
pub const ABI = struct {
all_versions: []const std.builtin.Version,
all_functions: []const Fn,
/// The value is a pointer to all_functions.len items and each item is an index into all_functions.
version_table: std.AutoHashMapUnmanaged(target_util.ArchOsAbi, [*]VerList),
arena_state: std.heap.ArenaAllocator.State,
pub fn destroy(abi: *ABI, gpa: *Allocator) void {
abi.version_table.deinit(gpa);
abi.arena_state.promote(gpa).deinit(); // Frees the ABI memory too.
}
};
pub const libs = [_]Lib{
.{ .name = "c", .sover = 6 },
.{ .name = "m", .sover = 6 },
.{ .name = "pthread", .sover = 0 },
.{ .name = "dl", .sover = 2 },
.{ .name = "rt", .sover = 1 },
.{ .name = "ld", .sover = 2 },
.{ .name = "util", .sover = 1 },
};
pub const LoadMetaDataError = error{
/// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data.
ZigInstallationCorrupt,
OutOfMemory,
};
/// This function will emit a log error when there is a problem with the zig installation and then return
/// `error.ZigInstallationCorrupt`.
pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError!*ABI {
const tracy = trace(@src());
defer tracy.end();
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
errdefer arena_allocator.deinit();
const arena = &arena_allocator.allocator;
var all_versions = std.ArrayListUnmanaged(std.builtin.Version){};
var all_functions = std.ArrayListUnmanaged(Fn){};
var version_table = std.AutoHashMapUnmanaged(target_util.ArchOsAbi, [*]VerList){};
errdefer version_table.deinit(gpa);
var glibc_dir = zig_lib_dir.openDir("libc" ++ path.sep_str ++ "glibc", .{}) catch |err| {
std.log.err("unable to open glibc dir: {}", .{@errorName(err)});
return error.ZigInstallationCorrupt;
};
defer glibc_dir.close();
const max_txt_size = 500 * 1024; // Bigger than this and something is definitely borked.
const vers_txt_contents = glibc_dir.readFileAlloc(gpa, "vers.txt", max_txt_size) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => {
std.log.err("unable to read vers.txt: {}", .{@errorName(err)});
return error.ZigInstallationCorrupt;
},
};
defer gpa.free(vers_txt_contents);
const fns_txt_contents = glibc_dir.readFileAlloc(gpa, "fns.txt", max_txt_size) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => {
std.log.err("unable to read fns.txt: {}", .{@errorName(err)});
return error.ZigInstallationCorrupt;
},
};
defer gpa.free(fns_txt_contents);
const abi_txt_contents = glibc_dir.readFileAlloc(gpa, "abi.txt", max_txt_size) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => {
std.log.err("unable to read abi.txt: {}", .{@errorName(err)});
return error.ZigInstallationCorrupt;
},
};
defer gpa.free(abi_txt_contents);
{
var it = mem.tokenize(vers_txt_contents, "\r\n");
var line_i: usize = 1;
while (it.next()) |line| : (line_i += 1) {
const prefix = "GLIBC_";
if (!mem.startsWith(u8, line, prefix)) {
std.log.err("vers.txt:{}: expected 'GLIBC_' prefix", .{line_i});
return error.ZigInstallationCorrupt;
}
const adjusted_line = line[prefix.len..];
const ver = std.builtin.Version.parse(adjusted_line) catch |err| {
std.log.err("vers.txt:{}: unable to parse glibc version '{}': {}", .{ line_i, line, @errorName(err) });
return error.ZigInstallationCorrupt;
};
try all_versions.append(arena, ver);
}
}
{
var file_it = mem.tokenize(fns_txt_contents, "\r\n");
var line_i: usize = 1;
while (file_it.next()) |line| : (line_i += 1) {
var line_it = mem.tokenize(line, " ");
const fn_name = line_it.next() orelse {
std.log.err("fns.txt:{}: expected function name", .{line_i});
return error.ZigInstallationCorrupt;
};
const lib_name = line_it.next() orelse {
std.log.err("fns.txt:{}: expected library name", .{line_i});
return error.ZigInstallationCorrupt;
};
const lib = findLib(lib_name) orelse {
std.log.err("fns.txt:{}: unknown library name: {}", .{ line_i, lib_name });
return error.ZigInstallationCorrupt;
};
try all_functions.append(arena, .{
.name = fn_name,
.lib = lib,
});
}
}
{
var file_it = mem.split(abi_txt_contents, "\n");
var line_i: usize = 0;
while (true) {
const ver_list_base: []VerList = blk: {
const line = file_it.next() orelse break;
if (line.len == 0) break;
line_i += 1;
const ver_list_base = try arena.alloc(VerList, all_functions.items.len);
var line_it = mem.tokenize(line, " ");
while (line_it.next()) |target_string| {
var component_it = mem.tokenize(target_string, "-");
const arch_name = component_it.next() orelse {
std.log.err("abi.txt:{}: expected arch name", .{line_i});
return error.ZigInstallationCorrupt;
};
const os_name = component_it.next() orelse {
std.log.err("abi.txt:{}: expected OS name", .{line_i});
return error.ZigInstallationCorrupt;
};
const abi_name = component_it.next() orelse {
std.log.err("abi.txt:{}: expected ABI name", .{line_i});
return error.ZigInstallationCorrupt;
};
const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse {
std.log.err("abi.txt:{}: unrecognized arch: '{}'", .{ line_i, arch_name });
return error.ZigInstallationCorrupt;
};
if (!mem.eql(u8, os_name, "linux")) {
std.log.err("abi.txt:{}: expected OS 'linux', found '{}'", .{ line_i, os_name });
return error.ZigInstallationCorrupt;
}
const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse {
std.log.err("abi.txt:{}: unrecognized ABI: '{}'", .{ line_i, abi_name });
return error.ZigInstallationCorrupt;
};
const triple = target_util.ArchOsAbi{
.arch = arch_tag,
.os = .linux,
.abi = abi_tag,
};
try version_table.put(arena, triple, ver_list_base.ptr);
}
break :blk ver_list_base;
};
for (ver_list_base) |*ver_list| {
const line = file_it.next() orelse {
std.log.err("abi.txt:{}: missing version number line", .{line_i});
return error.ZigInstallationCorrupt;
};
line_i += 1;
ver_list.* = .{
.versions = undefined,
.len = 0,
};
var line_it = mem.tokenize(line, " ");
while (line_it.next()) |version_index_string| {
if (ver_list.len >= ver_list.versions.len) {
// If this happens with legit data, increase the array len in the type.
std.log.err("abi.txt:{}: too many versions", .{line_i});
return error.ZigInstallationCorrupt;
}
const version_index = std.fmt.parseInt(u8, version_index_string, 10) catch |err| {
// If this happens with legit data, increase the size of the integer type in the struct.
std.log.err("abi.txt:{}: unable to parse version: {}", .{ line_i, @errorName(err) });
return error.ZigInstallationCorrupt;
};
ver_list.versions[ver_list.len] = version_index;
ver_list.len += 1;
}
}
}
}
const abi = try arena.create(ABI);
abi.* = .{
.all_versions = all_versions.items,
.all_functions = all_functions.items,
.version_table = version_table,
.arena_state = arena_allocator.state,
};
return abi;
}
fn findLib(name: []const u8) ?*const Lib {
for (libs) |*lib| {
if (mem.eql(u8, lib.name, name)) {
return lib;
}
}
return null;
}
pub const CRTFile = enum {
crti_o,
crtn_o,
start_os,
abi_note_o,
scrt1_o,
libc_nonshared_a,
};
pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
if (!build_options.have_llvm) {
return error.ZigCompilerNotBuiltWithLLVMExtensions;
}
const gpa = comp.gpa;
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
errdefer arena_allocator.deinit();
const arena = &arena_allocator.allocator;
switch (crt_file) {
.crti_o => {
var args = std.ArrayList([]const u8).init(arena);
try add_include_dirs(comp, arena, &args);
try args.appendSlice(&[_][]const u8{
"-D_LIBC_REENTRANT",
"-include",
try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
"-DMODULE_NAME=libc",
"-Wno-nonportable-include-path",
"-include",
try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
"-DTOP_NAMESPACE=glibc",
"-DASSEMBLER",
"-g",
"-Wa,--noexecstack",
});
const c_source_file: Compilation.CSourceFile = .{
.src_path = try start_asm_path(comp, arena, "crti.S"),
.extra_flags = args.items,
};
return build_libc_object(comp, "crti.o", c_source_file);
},
.crtn_o => {
var args = std.ArrayList([]const u8).init(arena);
try add_include_dirs(comp, arena, &args);
try args.appendSlice(&[_][]const u8{
"-D_LIBC_REENTRANT",
"-DMODULE_NAME=libc",
"-DTOP_NAMESPACE=glibc",
"-DASSEMBLER",
"-g",
"-Wa,--noexecstack",
});
const c_source_file: Compilation.CSourceFile = .{
.src_path = try start_asm_path(comp, arena, "crtn.S"),
.extra_flags = args.items,
};
return build_libc_object(comp, "crtn.o", c_source_file);
},
.start_os => {
var args = std.ArrayList([]const u8).init(arena);
try add_include_dirs(comp, arena, &args);
try args.appendSlice(&[_][]const u8{
"-D_LIBC_REENTRANT",
"-include",
try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
"-DMODULE_NAME=libc",
"-Wno-nonportable-include-path",
"-include",
try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
"-DPIC",
"-DSHARED",
"-DTOP_NAMESPACE=glibc",
"-DASSEMBLER",
"-g",
"-Wa,--noexecstack",
});
const c_source_file: Compilation.CSourceFile = .{
.src_path = try start_asm_path(comp, arena, "start.S"),
.extra_flags = args.items,
};
return build_libc_object(comp, "start.os", c_source_file);
},
.abi_note_o => {
var args = std.ArrayList([]const u8).init(arena);
try args.appendSlice(&[_][]const u8{
"-I",
try lib_path(comp, arena, lib_libc_glibc ++ "glibc" ++ path.sep_str ++ "csu"),
});
try add_include_dirs(comp, arena, &args);
try args.appendSlice(&[_][]const u8{
"-D_LIBC_REENTRANT",
"-DMODULE_NAME=libc",
"-DTOP_NAMESPACE=glibc",
"-DASSEMBLER",
"-g",
"-Wa,--noexecstack",
});
const c_source_file: Compilation.CSourceFile = .{
.src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "abi-note.S"),
.extra_flags = args.items,
};
return build_libc_object(comp, "abi-note.o", c_source_file);
},
.scrt1_o => {
return error.Unimplemented; // TODO
},
.libc_nonshared_a => {
return error.Unimplemented; // TODO
},
}
}
fn start_asm_path(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 {
const arch = comp.getTarget().cpu.arch;
const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le;
const is_aarch64 = arch == .aarch64 or arch == .aarch64_be;
const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9;
const is_64 = arch.ptrBitWidth() == 64;
const s = path.sep_str;
var result = std.ArrayList(u8).init(arena);
try result.appendSlice(comp.zig_lib_directory.path.?);
try result.appendSlice(s ++ "libc" ++ s ++ "glibc" ++ s ++ "sysdeps" ++ s);
if (is_sparc) {
if (is_64) {
try result.appendSlice("sparc" ++ s ++ "sparc64");
} else {
try result.appendSlice("sparc" ++ s ++ "sparc32");
}
} else if (arch.isARM()) {
try result.appendSlice("arm");
} else if (arch.isMIPS()) {
try result.appendSlice("mips");
} else if (arch == .x86_64) {
try result.appendSlice("x86_64");
} else if (arch == .i386) {
try result.appendSlice("i386");
} else if (is_aarch64) {
try result.appendSlice("aarch64");
} else if (arch.isRISCV()) {
try result.appendSlice("riscv");
} else if (is_ppc) {
if (is_64) {
try result.appendSlice("powerpc" ++ s ++ "powerpc64");
} else {
try result.appendSlice("powerpc" ++ s ++ "powerpc32");
}
}
try result.appendSlice(s);
try result.appendSlice(basename);
return result.items;
}
fn add_include_dirs(comp: *Compilation, arena: *Allocator, args: *std.ArrayList([]const u8)) error{OutOfMemory}!void {
const target = comp.getTarget();
const arch = target.cpu.arch;
const opt_nptl: ?[]const u8 = if (target.os.tag == .linux) "nptl" else "htl";
const glibc = try lib_path(comp, arena, lib_libc ++ "glibc");
const s = path.sep_str;
try args.append("-I");
try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "include"));
if (target.os.tag == .linux) {
try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv" ++ s ++ "linux"));
}
if (opt_nptl) |nptl| {
try add_include_dirs_arch(arena, args, arch, nptl, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps"));
}
if (target.os.tag == .linux) {
try args.append("-I");
try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
"unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "generic"));
try args.append("-I");
try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
"unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "include"));
try args.append("-I");
try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
"unix" ++ s ++ "sysv" ++ s ++ "linux"));
}
if (opt_nptl) |nptl| {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, lib_libc_glibc ++ "sysdeps", nptl }));
}
try args.append("-I");
try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "pthread"));
try args.append("-I");
try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv"));
try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix"));
try args.append("-I");
try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix"));
try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps"));
try args.append("-I");
try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "generic"));
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, lib_libc ++ "glibc" }));
try args.append("-I");
try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}", .{
comp.zig_lib_directory.path.?, @tagName(arch), @tagName(target.os.tag), @tagName(target.abi),
}));
try args.append("-I");
try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "generic-glibc"));
try args.append("-I");
try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-linux-any", .{
comp.zig_lib_directory.path.?, @tagName(arch),
}));
try args.append("-I");
try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "any-linux-any"));
}
fn add_include_dirs_arch(
arena: *Allocator,
args: *std.ArrayList([]const u8),
arch: std.Target.Cpu.Arch,
opt_nptl: ?[]const u8,
dir: []const u8,
) error{OutOfMemory}!void {
const is_x86 = arch == .i386 or arch == .x86_64;
const is_aarch64 = arch == .aarch64 or arch == .aarch64_be;
const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le;
const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9;
const is_64 = arch.ptrBitWidth() == 64;
const s = path.sep_str;
if (is_x86) {
if (arch == .x86_64) {
if (opt_nptl) |nptl| {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64", nptl }));
} else {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64" }));
}
} else if (arch == .i386) {
if (opt_nptl) |nptl| {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "i386", nptl }));
} else {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "i386" }));
}
}
if (opt_nptl) |nptl| {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "x86", nptl }));
} else {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "x86" }));
}
} else if (arch.isARM()) {
if (opt_nptl) |nptl| {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "arm", nptl }));
} else {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "arm" }));
}
} else if (arch.isMIPS()) {
if (opt_nptl) |nptl| {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "mips", nptl }));
} else {
if (is_64) {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" ++ s ++ "mips64" }));
} else {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" ++ s ++ "mips32" }));
}
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" }));
}
} else if (is_sparc) {
if (opt_nptl) |nptl| {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc", nptl }));
} else {
if (is_64) {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" ++ s ++ "sparc64" }));
} else {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" ++ s ++ "sparc32" }));
}
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" }));
}
} else if (is_aarch64) {
if (opt_nptl) |nptl| {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "aarch64", nptl }));
} else {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "aarch64" }));
}
} else if (is_ppc) {
if (opt_nptl) |nptl| {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc", nptl }));
} else {
if (is_64) {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" ++ s ++ "powerpc64" }));
} else {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" ++ s ++ "powerpc32" }));
}
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" }));
}
} else if (arch.isRISCV()) {
if (opt_nptl) |nptl| {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "riscv", nptl }));
} else {
try args.append("-I");
try args.append(try path.join(arena, &[_][]const u8{ dir, "riscv" }));
}
}
}
fn path_from_lib(comp: *Compilation, arena: *Allocator, sub_path: []const u8) ![]const u8 {
return path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, sub_path });
}
const lib_libc = "libc" ++ path.sep_str;
const lib_libc_glibc = lib_libc ++ "glibc" ++ path.sep_str;
fn lib_path(comp: *Compilation, arena: *Allocator, sub_path: []const u8) ![]const u8 {
return path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, sub_path });
}
fn build_libc_object(comp: *Compilation, basename: []const u8, c_source_file: Compilation.CSourceFile) !void {
const tracy = trace(@src());
defer tracy.end();
// TODO: This is extracted into a local variable to work around a stage1 miscompilation.
const emit_bin = Compilation.EmitLoc{
.directory = null, // Put it in the cache directory.
.basename = basename,
};
const sub_compilation = try Compilation.create(comp.gpa, .{
// TODO use the global cache directory here
.zig_cache_directory = comp.zig_cache_directory,
.zig_lib_directory = comp.zig_lib_directory,
.target = comp.getTarget(),
.root_name = mem.split(basename, ".").next().?,
.root_pkg = null,
.output_mode = .Obj,
.rand = comp.rand,
.libc_installation = comp.bin_file.options.libc_installation,
.emit_bin = emit_bin,
.optimize_mode = comp.bin_file.options.optimize_mode,
.want_sanitize_c = false,
.want_stack_check = false,
.want_valgrind = false,
.want_pic = comp.bin_file.options.pic,
.emit_h = null,
.strip = comp.bin_file.options.strip,
.is_native_os = comp.bin_file.options.is_native_os,
.self_exe_path = comp.self_exe_path,
.c_source_files = &[1]Compilation.CSourceFile{c_source_file},
.debug_cc = comp.debug_cc,
.debug_link = comp.bin_file.options.debug_link,
.clang_passthrough_mode = comp.clang_passthrough_mode,
});
defer sub_compilation.destroy();
try sub_compilation.update();
// Look for compilation errors in this sub_compilation
var errors = try sub_compilation.getAllErrorsAlloc();
defer errors.deinit(sub_compilation.gpa);
if (errors.list.len != 0) {
for (errors.list) |full_err_msg| {
std.log.err("{}:{}:{}: error: {}\n", .{
full_err_msg.src_path,
full_err_msg.line + 1,
full_err_msg.column + 1,
full_err_msg.msg,
});
}
return error.BuildingLibCObjectFailed;
}
try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1);
const artifact_path = if (sub_compilation.bin_file.options.directory.path) |p|
try std.fs.path.join(comp.gpa, &[_][]const u8{ p, basename })
else
try comp.gpa.dupe(u8, basename);
// TODO obtain a lock on the artifact and put that in crt_files as well.
comp.crt_files.putAssumeCapacityNoClobber(basename, artifact_path);
}