Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2397,7 +2397,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {

// Work around windows `AccessDenied` if any files within this
// directory are open by closing and reopening the file handles.
const need_writable_dance = w: {
const need_writable_dance: enum { no, lf_only, lf_and_debug } = w: {
if (builtin.os.tag == .windows) {
if (comp.bin_file) |lf| {
// We cannot just call `makeExecutable` as it makes a false
Expand All @@ -2410,11 +2410,13 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
if (lf.file) |f| {
f.close();
lf.file = null;
break :w true;

if (lf.closeDebugInfo()) break :w .lf_and_debug;
break :w .lf_only;
}
}
}
break :w false;
break :w .no;
};

renameTmpIntoCache(comp.local_cache_directory, tmp_dir_sub_path, o_sub_path) catch |err| {
Expand All @@ -2441,8 +2443,13 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
};

// Has to be after the `wholeCacheModeSetBinFilePath` above.
if (need_writable_dance) {
try lf.makeWritable();
switch (need_writable_dance) {
.no => {},
.lf_only => try lf.makeWritable(),
.lf_and_debug => {
try lf.makeWritable();
try lf.reopenDebugInfo();
},
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/link.zig
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,20 @@ pub const File = struct {
}
}

/// Some linkers create a separate file for debug info, which we might need to temporarily close
/// when moving the compilation result directory due to the host OS not allowing moving a
/// file/directory while a handle remains open.
/// Returns `true` if a debug info file was closed. In that case, `reopenDebugInfo` may be called.
pub fn closeDebugInfo(base: *File) bool {
const macho = base.cast(.macho) orelse return false;
return macho.closeDebugInfo();
}

pub fn reopenDebugInfo(base: *File) !void {
const macho = base.cast(.macho).?;
return macho.reopenDebugInfo();
}

pub fn makeExecutable(base: *File) !void {
dev.check(.make_executable);
const comp = base.comp;
Expand Down
52 changes: 33 additions & 19 deletions src/link/MachO.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3277,6 +3277,37 @@ const InitMetadataOptions = struct {
program_code_size_hint: u64,
};

pub fn closeDebugInfo(self: *MachO) bool {
const d_sym = &(self.d_sym orelse return false);
d_sym.file.?.close();
d_sym.file = null;
return true;
}

pub fn reopenDebugInfo(self: *MachO) !void {
assert(self.d_sym.?.file == null);

assert(!self.base.comp.config.use_llvm);
assert(self.base.comp.config.debug_format == .dwarf);

const gpa = self.base.comp.gpa;
const sep = fs.path.sep_str;
const d_sym_path = try std.fmt.allocPrint(
gpa,
"{s}.dSYM" ++ sep ++ "Contents" ++ sep ++ "Resources" ++ sep ++ "DWARF",
.{self.base.emit.sub_path},
);
defer gpa.free(d_sym_path);

var d_sym_bundle = try self.base.emit.root_dir.handle.makeOpenPath(d_sym_path, .{});
defer d_sym_bundle.close();

self.d_sym.?.file = try d_sym_bundle.createFile(fs.path.basename(self.base.emit.sub_path), .{
.truncate = false,
.read = true,
});
}

// TODO: move to ZigObject
fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
if (!self.base.isRelocatable()) {
Expand Down Expand Up @@ -3333,25 +3364,8 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
if (options.zo.dwarf) |*dwarf| {
// Create dSYM bundle.
log.debug("creating {s}.dSYM bundle", .{options.emit.sub_path});

const gpa = self.base.comp.gpa;
const sep = fs.path.sep_str;
const d_sym_path = try std.fmt.allocPrint(
gpa,
"{s}.dSYM" ++ sep ++ "Contents" ++ sep ++ "Resources" ++ sep ++ "DWARF",
.{options.emit.sub_path},
);
defer gpa.free(d_sym_path);

var d_sym_bundle = try options.emit.root_dir.handle.makeOpenPath(d_sym_path, .{});
defer d_sym_bundle.close();

const d_sym_file = try d_sym_bundle.createFile(options.emit.sub_path, .{
.truncate = false,
.read = true,
});

self.d_sym = .{ .allocator = gpa, .file = d_sym_file };
self.d_sym = .{ .allocator = self.base.comp.gpa, .file = null };
try self.reopenDebugInfo();
try self.d_sym.?.initMetadata(self);
try dwarf.initMetadata();
}
Expand Down
22 changes: 11 additions & 11 deletions src/link/MachO/DebugSymbols.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
allocator: Allocator,
file: fs.File,
file: ?fs.File,

symtab_cmd: macho.symtab_command = .{},
uuid_cmd: macho.uuid_command = .{ .uuid = [_]u8{0} ** 16 },
Expand Down Expand Up @@ -118,9 +118,9 @@ pub fn growSection(
});

if (requires_file_copy) {
const amt = try self.file.copyRangeAll(
const amt = try self.file.?.copyRangeAll(
sect.offset,
self.file,
self.file.?,
new_offset,
existing_size,
);
Expand All @@ -129,7 +129,7 @@ pub fn growSection(

sect.offset = @intCast(new_offset);
} else if (sect.offset + allocated_size == std.math.maxInt(u64)) {
try self.file.setEndPos(sect.offset + needed_size);
try self.file.?.setEndPos(sect.offset + needed_size);
}

sect.size = needed_size;
Expand Down Expand Up @@ -165,7 +165,7 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) !?u64 {
}
}

if (at_end) try self.file.setEndPos(end);
if (at_end) try self.file.?.setEndPos(end);
return null;
}

Expand Down Expand Up @@ -195,7 +195,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
sym_name,
file_offset,
});
try self.file.pwriteAll(mem.asBytes(&addr), file_offset);
try self.file.?.pwriteAll(mem.asBytes(&addr), file_offset);
}

self.finalizeDwarfSegment(macho_file);
Expand All @@ -208,7 +208,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {

pub fn deinit(self: *DebugSymbols) void {
const gpa = self.allocator;
self.file.close();
if (self.file) |file| file.close();
self.segments.deinit(gpa);
self.sections.deinit(gpa);
self.relocs.deinit(gpa);
Expand Down Expand Up @@ -320,7 +320,7 @@ fn writeLoadCommands(self: *DebugSymbols, macho_file: *MachO) !struct { usize, u

assert(stream.pos == needed_size);

try self.file.pwriteAll(buffer, @sizeOf(macho.mach_header_64));
try self.file.?.pwriteAll(buffer, @sizeOf(macho.mach_header_64));

return .{ ncmds, buffer.len };
}
Expand All @@ -346,7 +346,7 @@ fn writeHeader(self: *DebugSymbols, macho_file: *MachO, ncmds: usize, sizeofcmds

log.debug("writing Mach-O header {}", .{header});

try self.file.pwriteAll(mem.asBytes(&header), 0);
try self.file.?.pwriteAll(mem.asBytes(&header), 0);
}

fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
Expand Down Expand Up @@ -404,15 +404,15 @@ pub fn writeSymtab(self: *DebugSymbols, off: u32, macho_file: *MachO) !u32 {
internal.writeSymtab(macho_file, self);
}

try self.file.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);
try self.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);

return off + cmd.nsyms * @sizeOf(macho.nlist_64);
}

pub fn writeStrtab(self: *DebugSymbols, off: u32) !u32 {
const cmd = &self.symtab_cmd;
cmd.stroff = off;
try self.file.pwriteAll(self.strtab.items, cmd.stroff);
try self.file.?.pwriteAll(self.strtab.items, cmd.stroff);
return off + cmd.strsize;
}

Expand Down
45 changes: 18 additions & 27 deletions src/link/SpirV.zig
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ pub fn createEmpty(
const gpa = comp.gpa;
const target = comp.root_mod.resolved_target.result;

assert(!comp.config.use_lld); // Caught by Compilation.Config.resolve
assert(!comp.config.use_llvm); // Caught by Compilation.Config.resolve
assert(target.ofmt == .spirv); // Caught by Compilation.Config.resolve
switch (target.cpu.arch) {
.spirv, .spirv32, .spirv64 => {},
else => unreachable, // Caught by Compilation.Config.resolve.
}
switch (target.os.tag) {
.opencl, .opengl, .vulkan => {},
else => unreachable, // Caught by Compilation.Config.resolve.
}

const self = try arena.create(SpirV);
self.* = .{
.base = .{
Expand All @@ -79,15 +91,11 @@ pub fn createEmpty(
};
errdefer self.deinit();

switch (target.cpu.arch) {
.spirv, .spirv32, .spirv64 => {},
else => unreachable, // Caught by Compilation.Config.resolve.
}

switch (target.os.tag) {
.opencl, .opengl, .vulkan => {},
else => unreachable, // Caught by Compilation.Config.resolve.
}
// TODO: read the file and keep valid parts instead of truncating
self.base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{
.truncate = true,
.read = true,
});

return self;
}
Expand All @@ -98,24 +106,7 @@ pub fn open(
emit: Path,
options: link.File.OpenOptions,
) !*SpirV {
const target = comp.root_mod.resolved_target.result;
const use_lld = build_options.have_llvm and comp.config.use_lld;
const use_llvm = comp.config.use_llvm;

assert(!use_llvm); // Caught by Compilation.Config.resolve.
assert(!use_lld); // Caught by Compilation.Config.resolve.
assert(target.ofmt == .spirv); // Caught by Compilation.Config.resolve.

const spirv = try createEmpty(arena, comp, emit, options);
errdefer spirv.base.destroy();

// TODO: read the file and keep valid parts instead of truncating
const file = try emit.root_dir.handle.createFile(emit.sub_path, .{
.truncate = true,
.read = true,
});
spirv.base.file = file;
return spirv;
return createEmpty(arena, comp, emit, options);
}

pub fn deinit(self: *SpirV) void {
Expand Down
6 changes: 5 additions & 1 deletion src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3470,7 +3470,11 @@ fn buildOutputType(
// incremental cache mode is used for LLVM backend too.
if (create_module.resolved_options.use_llvm) break :b .whole;

break :b .incremental;
// Eventually, this default should be `.incremental`. However, since incremental
// compilation is currently an opt-in feature, it makes a strictly worse default cache mode
// than `.whole`.
// https://github.com/ziglang/zig/issues/21165
break :b .whole;
};

process.raiseFileDescriptorLimit();
Expand Down