Commit 2627778b25

Andrew Kelley <andrew@ziglang.org>
2020-09-14 07:15:03
stage2: don't create empty object files when no zig source
1 parent 046dce9
src-self-hosted/link/C.zig
@@ -23,7 +23,7 @@ need_stddef: bool = false,
 need_stdint: bool = false,
 error_msg: *Compilation.ErrorMsg = undefined,
 
-pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File {
+pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C {
     assert(options.object_format == .c);
 
     if (options.use_llvm) return error.LLVMHasNoCBackend;
@@ -48,7 +48,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
         .called = std.StringHashMap(void).init(allocator),
     };
 
-    return &c_file.base;
+    return c_file;
 }
 
 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
src-self-hosted/link/Coff.zig
@@ -28,7 +28,7 @@ pub const base_tag: link.File.Tag = .coff;
 const msdos_stub = @embedFile("msdos-stub.bin");
 
 base: link.File,
-ptr_width: enum { p32, p64 },
+ptr_width: PtrWidth,
 error_flags: link.File.ErrorFlags = .{},
 
 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
@@ -64,6 +64,8 @@ text_section_size_dirty: bool = false,
 /// and needs to be updated in the optional header.
 size_of_image_dirty: bool = false,
 
+pub const PtrWidth = enum { p32, p64 };
+
 pub const TextBlock = struct {
     /// Offset of the code relative to the start of the text section
     text_offset: u32,
@@ -111,7 +113,7 @@ pub const TextBlock = struct {
 
 pub const SrcFn = void;
 
-pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*link.File {
+pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Coff {
     assert(options.object_format == .coff);
 
     if (options.use_llvm) return error.LLVM_BackendIsTODO_ForCoff; // TODO
@@ -124,66 +126,17 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
     });
     errdefer file.close();
 
-    var coff_file = try allocator.create(Coff);
-    errdefer allocator.destroy(coff_file);
-
-    coff_file.* = openFile(allocator, file, options) catch |err| switch (err) {
-        error.IncrFailed => try createFile(allocator, file, options),
-        else => |e| return e,
-    };
-
-    return &coff_file.base;
-}
-
-/// Returns error.IncrFailed if incremental update could not be performed.
-fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff {
-    switch (options.output_mode) {
-        .Exe => {},
-        .Obj => return error.IncrFailed,
-        .Lib => return error.IncrFailed,
-    }
-    var self: Coff = .{
-        .base = .{
-            .file = file,
-            .tag = .coff,
-            .options = options,
-            .allocator = allocator,
-        },
-        .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
-            32 => .p32,
-            64 => .p64,
-            else => return error.UnsupportedELFArchitecture,
-        },
-    };
-    errdefer self.deinit();
+    const self = try createEmpty(allocator, options);
+    errdefer self.base.destroy();
 
-    // TODO implement reading the PE/COFF file
-    return error.IncrFailed;
-}
+    self.base.file = file;
 
-/// Truncates the existing file contents and overwrites the contents.
-/// Returns an error if `file` is not already open with +read +write +seek abilities.
-fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff {
     // TODO Write object specific relocations, COFF symbol table, then enable object file output.
     switch (options.output_mode) {
         .Exe => {},
         .Obj => return error.TODOImplementWritingObjFiles,
         .Lib => return error.TODOImplementWritingLibFiles,
     }
-    var self: Coff = .{
-        .base = .{
-            .tag = .coff,
-            .options = options,
-            .allocator = allocator,
-            .file = file,
-        },
-        .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
-            32 => .p32,
-            64 => .p64,
-            else => return error.UnsupportedCOFFArchitecture,
-        },
-    };
-    errdefer self.deinit();
 
     var coff_file_header_offset: u32 = 0;
     if (options.output_mode == .Exe) {
@@ -426,6 +379,25 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff
     return self;
 }
 
+pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff {
+    const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) {
+        0...32 => .p32,
+        33...64 => .p64,
+        else => return error.UnsupportedCOFFArchitecture,
+    };
+    const self = try gpa.create(Coff);
+    self.* = .{
+        .base = .{
+            .tag = .coff,
+            .options = options,
+            .allocator = gpa,
+            .file = null,
+        },
+        .ptr_width = ptr_width,
+    };
+    return self;
+}
+
 pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void {
     try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
 
src-self-hosted/link/Elf.zig
@@ -31,7 +31,7 @@ pub const base_tag: File.Tag = .elf;
 
 base: File,
 
-ptr_width: enum { p32, p64 },
+ptr_width: PtrWidth,
 
 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
 /// Same order as in the file.
@@ -136,6 +136,8 @@ const alloc_den = 3;
 const minimum_text_block_size = 64;
 const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
 
+pub const PtrWidth = enum { p32, p64 };
+
 pub const TextBlock = struct {
     /// Each decl always gets a local symbol with the fully qualified name.
     /// The vaddr and size are found here directly.
@@ -222,7 +224,7 @@ pub const SrcFn = struct {
     };
 };
 
-pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File {
+pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Elf {
     assert(options.object_format == .elf);
 
     if (options.use_llvm) return error.LLVMBackendUnimplementedForELF; // TODO
@@ -234,31 +236,11 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
     });
     errdefer file.close();
 
-    var elf_file = try allocator.create(Elf);
-    errdefer allocator.destroy(elf_file);
-
-    elf_file.* = try createFile(allocator, file, options);
-    return &elf_file.base;
-}
+    const self = try createEmpty(allocator, options);
+    errdefer self.base.destroy();
 
-/// Truncates the existing file contents and overwrites the contents.
-/// Returns an error if `file` is not already open with +read +write +seek abilities.
-fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf {
-    var self: Elf = .{
-        .base = .{
-            .tag = .elf,
-            .options = options,
-            .allocator = allocator,
-            .file = file,
-        },
-        .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
-            0 ... 32 => .p32,
-            33 ... 64 => .p64,
-            else => return error.UnsupportedELFArchitecture,
-        },
-        .shdr_table_dirty = true,
-    };
-    errdefer self.deinit();
+    self.base.file = file;
+    self.shdr_table_dirty = true;
 
     // Index 0 is always a null symbol.
     try self.local_symbols.append(allocator, .{
@@ -289,6 +271,25 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf
     return self;
 }
 
+pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf {
+    const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) {
+        0 ... 32 => .p32,
+        33 ... 64 => .p64,
+        else => return error.UnsupportedELFArchitecture,
+    };
+    const self = try gpa.create(Elf);
+    self.* = .{
+        .base = .{
+            .tag = .elf,
+            .options = options,
+            .allocator = gpa,
+            .file = null,
+        },
+        .ptr_width = ptr_width,
+    };
+    return self;
+}
+
 pub fn releaseLock(self: *Elf) void {
     if (self.lock) |*lock| {
         lock.release();
src-self-hosted/link/MachO.zig
@@ -135,7 +135,7 @@ pub const SrcFn = struct {
     pub const empty = SrcFn{};
 };
 
-pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File {
+pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*MachO {
     assert(options.object_format == .macho);
 
     if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO
@@ -148,61 +148,32 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
     });
     errdefer file.close();
 
-    var macho_file = try allocator.create(MachO);
-    errdefer allocator.destroy(macho_file);
+    const self = try createEmpty(allocator, options);
+    errdefer self.base.destroy();
 
-    macho_file.* = openFile(allocator, file, options) catch |err| switch (err) {
-        error.IncrFailed => try createFile(allocator, file, options),
-        else => |e| return e,
-    };
-
-    return &macho_file.base;
-}
+    self.base.file = file;
 
-/// Returns error.IncrFailed if incremental update could not be performed.
-fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO {
     switch (options.output_mode) {
         .Exe => {},
         .Obj => {},
-        .Lib => return error.IncrFailed,
+        .Lib => return error.TODOImplementWritingLibFiles,
     }
-    var self: MachO = .{
-        .base = .{
-            .file = file,
-            .tag = .macho,
-            .options = options,
-            .allocator = allocator,
-        },
-    };
-    errdefer self.deinit();
 
-    // TODO implement reading the macho file
-    return error.IncrFailed;
-    //try self.populateMissingMetadata();
-    //return self;
-}
+    try self.populateMissingMetadata();
 
-/// Truncates the existing file contents and overwrites the contents.
-/// Returns an error if `file` is not already open with +read +write +seek abilities.
-fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO {
-    switch (options.output_mode) {
-        .Exe => {},
-        .Obj => {},
-        .Lib => return error.TODOImplementWritingLibFiles,
-    }
+    return self;
+}
 
-    var self: MachO = .{
+pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO {
+    const self = try gpa.create(MachO);
+    self.* = .{
         .base = .{
-            .file = file,
             .tag = .macho,
             .options = options,
-            .allocator = allocator,
+            .allocator = gpa,
+            .file = null,
         },
     };
-    errdefer self.deinit();
-
-    try self.populateMissingMetadata();
-
     return self;
 }
 
src-self-hosted/link/Wasm.zig
@@ -50,7 +50,7 @@ base: link.File,
 /// TODO: can/should we access some data structure in Module directly?
 funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
 
-pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*link.File {
+pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm {
     assert(options.object_format == .wasm);
 
     if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO
@@ -60,21 +60,27 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
     const file = try options.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
     errdefer file.close();
 
-    const wasm = try allocator.create(Wasm);
-    errdefer allocator.destroy(wasm);
+    const wasm = try createEmpty(allocator, options);
+    errdefer wasm.base.destroy();
+
+    wasm.base.file = file;
 
     try file.writeAll(&(spec.magic ++ spec.version));
 
+    return wasm;
+}
+
+pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm {
+    const wasm = try gpa.create(Wasm);
     wasm.* = .{
         .base = .{
             .tag = .wasm,
             .options = options,
-            .file = file,
-            .allocator = allocator,
+            .file = null,
+            .allocator = gpa,
         },
     };
-
-    return &wasm.base;
+    return wasm;
 }
 
 pub fn deinit(self: *Wasm) void {
src-self-hosted/Compilation.zig
@@ -1072,13 +1072,14 @@ fn updateCObject(comp: *Compilation, c_object: *CObject) !void {
         break :blk digest;
     };
 
-    const full_object_path = if (comp.zig_cache_directory.path) |p|
-        try std.fs.path.join(comp.gpa, &[_][]const u8{ p, "o", &digest, o_basename })
+    const components = if (comp.zig_cache_directory.path) |p|
+        &[_][]const u8{ p, "o", &digest, o_basename }
     else
-        try std.fs.path.join(comp.gpa, &[_][]const u8{ "o", &digest, o_basename });
+        &[_][]const u8{ "o", &digest, o_basename };
+
     c_object.status = .{
         .success = .{
-            .object_path = full_object_path,
+            .object_path = try std.fs.path.join(comp.gpa, components),
             .lock = ch.toOwnedLock(),
         },
     };
src-self-hosted/link.zig
@@ -126,17 +126,29 @@ pub const File = struct {
     pub fn openPath(allocator: *Allocator, options: Options) !*File {
         const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm
         const sub_path = if (use_lld) blk: {
+            if (options.module == null) {
+                // No point in opening a file, we would not write anything to it. Initialize with empty.
+                return switch (options.object_format) {
+                    .coff, .pe => &(try Coff.createEmpty(allocator, options)).base,
+                    .elf => &(try Elf.createEmpty(allocator, options)).base,
+                    .macho => &(try MachO.createEmpty(allocator, options)).base,
+                    .wasm => &(try Wasm.createEmpty(allocator, options)).base,
+                    .c => unreachable, // Reported error earlier.
+                    .hex => return error.HexObjectFormatUnimplemented,
+                    .raw => return error.RawObjectFormatUnimplemented,
+                };
+            }
             // Open a temporary object file, not the final output file because we want to link with LLD.
             break :blk try std.fmt.allocPrint(allocator, "{}{}", .{ options.sub_path, options.target.oFileExt() });
         } else options.sub_path;
         errdefer if (use_lld) allocator.free(sub_path);
 
         const file: *File = switch (options.object_format) {
-            .coff, .pe => try Coff.openPath(allocator, sub_path, options),
-            .elf => try Elf.openPath(allocator, sub_path, options),
-            .macho => try MachO.openPath(allocator, sub_path, options),
-            .wasm => try Wasm.openPath(allocator, sub_path, options),
-            .c => try C.openPath(allocator, sub_path, options),
+            .coff, .pe => &(try Coff.openPath(allocator, sub_path, options)).base,
+            .elf => &(try Elf.openPath(allocator, sub_path, options)).base,
+            .macho => &(try MachO.openPath(allocator, sub_path, options)).base,
+            .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base,
+            .c => &(try C.openPath(allocator, sub_path, options)).base,
             .hex => return error.HexObjectFormatUnimplemented,
             .raw => return error.RawObjectFormatUnimplemented,
         };
@@ -216,19 +228,9 @@ pub const File = struct {
         }
     }
 
-    pub fn deinit(base: *File) void {
-        switch (base.tag) {
-            .coff => @fieldParentPtr(Coff, "base", base).deinit(),
-            .elf => @fieldParentPtr(Elf, "base", base).deinit(),
-            .macho => @fieldParentPtr(MachO, "base", base).deinit(),
-            .c => @fieldParentPtr(C, "base", base).deinit(),
-            .wasm => @fieldParentPtr(Wasm, "base", base).deinit(),
-        }
+    pub fn destroy(base: *File) void {
         if (base.file) |f| f.close();
         if (base.intermediary_basename) |sub_path| base.allocator.free(sub_path);
-    }
-
-    pub fn destroy(base: *File) void {
         switch (base.tag) {
             .coff => {
                 const parent = @fieldParentPtr(Coff, "base", base);