Commit 12de7e3472
Changed files (16)
lib
std
Build
src
codegen
Compilation
Package
lib/std/Build/Cache.zig
@@ -312,6 +312,20 @@ pub const HashHelper = struct {
) catch unreachable;
return out_digest;
}
+
+ pub fn oneShot(bytes: []const u8) [hex_digest_len]u8 {
+ var hasher: Hasher = hasher_init;
+ hasher.update(bytes);
+ var bin_digest: BinDigest = undefined;
+ hasher.final(&bin_digest);
+ var out_digest: [hex_digest_len]u8 = undefined;
+ _ = fmt.bufPrint(
+ &out_digest,
+ "{s}",
+ .{fmt.fmtSliceHexLower(&bin_digest)},
+ ) catch unreachable;
+ return out_digest;
+ }
};
pub const Lock = struct {
lib/std/Build/Module.zig
@@ -34,6 +34,7 @@ valgrind: ?bool,
pic: ?bool,
red_zone: ?bool,
omit_frame_pointer: ?bool,
+error_tracing: ?bool,
link_libc: ?bool,
link_libcpp: ?bool,
@@ -177,6 +178,7 @@ pub const CreateOptions = struct {
/// Whether to omit the stack frame pointer. Frees up a register and makes it
/// more difficult to obtain stack traces. Has target-dependent effects.
omit_frame_pointer: ?bool = null,
+ error_tracing: ?bool = null,
};
pub const Import = struct {
@@ -216,6 +218,7 @@ pub fn init(m: *Module, owner: *std.Build, options: CreateOptions, compile: ?*St
.pic = options.pic,
.red_zone = options.red_zone,
.omit_frame_pointer = options.omit_frame_pointer,
+ .error_tracing = options.error_tracing,
.export_symbol_names = &.{},
};
@@ -601,6 +604,7 @@ pub fn appendZigProcessFlags(
try addFlag(zig_args, m.stack_check, "-fstack-check", "-fno-stack-check");
try addFlag(zig_args, m.stack_protector, "-fstack-protector", "-fno-stack-protector");
try addFlag(zig_args, m.omit_frame_pointer, "-fomit-frame-pointer", "-fno-omit-frame-pointer");
+ try addFlag(zig_args, m.error_tracing, "-ferror-tracing", "-fno-error-tracing");
try addFlag(zig_args, m.sanitize_c, "-fsanitize-c", "-fno-sanitize-c");
try addFlag(zig_args, m.sanitize_thread, "-fsanitize-thread", "-fno-sanitize-thread");
try addFlag(zig_args, m.valgrind, "-fvalgrind", "-fno-valgrind");
src/codegen/llvm.zig
@@ -853,16 +853,9 @@ pub const Object = struct {
/// want to iterate over it while adding entries to it.
pub const DITypeMap = std.AutoArrayHashMapUnmanaged(InternPool.Index, AnnotatedDITypePtr);
- pub fn create(gpa: Allocator, options: link.Options) !*Object {
- const obj = try gpa.create(Object);
- errdefer gpa.destroy(obj);
- obj.* = try Object.init(gpa, options);
- return obj;
- }
-
- pub fn init(gpa: Allocator, options: link.Options) !Object {
- const llvm_target_triple = try targetTriple(gpa, options.target);
- defer gpa.free(llvm_target_triple);
+ pub fn create(arena: Allocator, options: link.File.OpenOptions) !*Object {
+ const gpa = options.comp.gpa;
+ const llvm_target_triple = try targetTriple(arena, options.target);
var builder = try Builder.init(.{
.allocator = gpa,
@@ -899,19 +892,14 @@ pub const Object = struct {
// TODO: the only concern I have with this is WASI as either host or target, should
// we leave the paths as relative then?
const compile_unit_dir_z = blk: {
- var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
if (options.module) |mod| m: {
- const d = try mod.root_mod.root.joinStringZ(builder.gpa, "");
+ const d = try mod.root_mod.root.joinStringZ(arena, "");
if (d.len == 0) break :m;
if (std.fs.path.isAbsolute(d)) break :blk d;
- const abs = std.fs.realpath(d, &buf) catch break :blk d;
- builder.gpa.free(d);
- break :blk try builder.gpa.dupeZ(u8, abs);
+ break :blk std.fs.realpathAlloc(arena, d) catch d;
}
- const cwd = try std.process.getCwd(&buf);
- break :blk try builder.gpa.dupeZ(u8, cwd);
+ break :blk try std.process.getCwdAlloc(arena);
};
- defer builder.gpa.free(compile_unit_dir_z);
builder.llvm.di_compile_unit = builder.llvm.di_builder.?.createCompileUnit(
DW.LANG.C99,
@@ -989,7 +977,8 @@ pub const Object = struct {
}
}
- return .{
+ const obj = try arena.create(Object);
+ obj.* = .{
.gpa = gpa,
.builder = builder,
.module = options.module.?,
@@ -1009,9 +998,11 @@ pub const Object = struct {
.null_opt_usize = .no_init,
.struct_field_map = .{},
};
+ return obj;
}
- pub fn deinit(self: *Object, gpa: Allocator) void {
+ pub fn deinit(self: *Object) void {
+ const gpa = self.gpa;
self.di_map.deinit(gpa);
self.di_type_map.deinit(gpa);
if (self.builder.useLibLlvm()) {
@@ -1028,11 +1019,6 @@ pub const Object = struct {
self.* = undefined;
}
- pub fn destroy(self: *Object, gpa: Allocator) void {
- self.deinit(gpa);
- gpa.destroy(self);
- }
-
fn locPath(
arena: Allocator,
opt_loc: ?Compilation.EmitLoc,
@@ -2899,7 +2885,7 @@ pub const Object = struct {
fn getStackTraceType(o: *Object) Allocator.Error!Type {
const mod = o.module;
- const std_mod = mod.main_mod.deps.get("std").?;
+ const std_mod = mod.std_mod;
const std_file = (mod.importPkg(std_mod) catch unreachable).file;
const builtin_str = try mod.intern_pool.getOrPutString(mod.gpa, "builtin");
src/Compilation/Config.zig
@@ -0,0 +1,382 @@
+//! User-specified settings that have all the defaults resolved into concrete values.
+
+have_zcu: bool,
+output_mode: std.builtin.OutputMode,
+link_mode: std.builtin.LinkMode,
+link_libc: bool,
+link_libcpp: bool,
+link_libunwind: bool,
+any_unwind_tables: bool,
+pie: bool,
+/// If this is true then linker code is responsible for making an LLVM IR
+/// Module, outputting it to an object file, and then linking that together
+/// with link options and other objects. Otherwise (depending on `use_lld`)
+/// linker code directly outputs and updates the final binary.
+use_llvm: bool,
+/// Whether or not the LLVM library API will be used by the LLVM backend.
+use_lib_llvm: bool,
+/// If this is true then linker code is responsible for outputting an object
+/// file and then using LLD to link it together with the link options and other
+/// objects. Otherwise (depending on `use_llvm`) linker code directly outputs
+/// and updates the final binary.
+use_lld: bool,
+c_frontend: CFrontend,
+lto: bool,
+/// WASI-only. Type of WASI execution model ("command" or "reactor").
+/// Always set to `command` for non-WASI targets.
+wasi_exec_model: std.builtin.WasiExecModel,
+import_memory: bool,
+export_memory: bool,
+shared_memory: bool,
+is_test: bool,
+test_evented_io: bool,
+entry: ?[]const u8,
+
+pub const CFrontend = enum { clang, aro };
+
+pub const Options = struct {
+ output_mode: std.builtin.OutputMode,
+ resolved_target: Module.ResolvedTarget,
+ is_test: bool,
+ have_zcu: bool,
+ emit_bin: bool,
+ root_optimize_mode: ?std.builtin.OptimizeMode = null,
+ link_mode: ?std.builtin.LinkMode = null,
+ ensure_libc_on_non_freestanding: bool = false,
+ ensure_libcpp_on_non_freestanding: bool = false,
+ any_non_single_threaded: bool = false,
+ any_sanitize_thread: bool = false,
+ any_unwind_tables: bool = false,
+ any_dyn_libs: bool = false,
+ c_source_files_len: usize = 0,
+ emit_llvm_ir: bool = false,
+ emit_llvm_bc: bool = false,
+ link_libc: ?bool = null,
+ link_libcpp: ?bool = null,
+ link_libunwind: ?bool = null,
+ pie: ?bool = null,
+ use_llvm: ?bool = null,
+ use_lib_llvm: ?bool = null,
+ use_lld: ?bool = null,
+ use_clang: ?bool = null,
+ lto: ?bool = null,
+ entry: union(enum) {
+ default,
+ disabled,
+ enabled,
+ named: []const u8,
+ } = .default,
+ /// WASI-only. Type of WASI execution model ("command" or "reactor").
+ wasi_exec_model: ?std.builtin.WasiExecModel = null,
+ import_memory: ?bool = null,
+ export_memory: ?bool = null,
+ shared_memory: ?bool = null,
+ test_evented_io: bool = false,
+};
+
+pub fn resolve(options: Options) !Config {
+ const target = options.resolved_target.result;
+
+ // WASI-only. Resolve the optional exec-model option, defaults to command.
+ if (target.os.tag != .wasi and options.wasi_exec_model != null)
+ return error.WasiExecModelRequiresWasi;
+ const wasi_exec_model = options.wasi_exec_model orelse .command;
+
+ const shared_memory = b: {
+ if (!target.cpu.arch.isWasm()) {
+ if (options.shared_memory == true) return error.SharedMemoryIsWasmOnly;
+ break :b false;
+ }
+ if (options.output_mode == .Obj) {
+ if (options.shared_memory == true) return error.ObjectFilesCannotShareMemory;
+ break :b false;
+ }
+ if (!std.Target.wasm.featureSetHasAll(target.cpu.features, .{ .atomics, .bulk_memory })) {
+ if (options.shared_memory == true)
+ return error.SharedMemoryRequiresAtomicsAndBulkMemory;
+ break :b false;
+ }
+ if (options.any_non_single_threaded) {
+ if (options.shared_memory == false)
+ return error.ThreadsRequireSharedMemory;
+ break :b true;
+ }
+ break :b options.shared_memory orelse false;
+ };
+
+ const entry: ?[]const u8 = switch (options.entry) {
+ .disabled => null,
+ .default => b: {
+ if (options.output_mode != .Exe) break :b null;
+ break :b target_util.defaultEntrySymbolName(target, wasi_exec_model) orelse
+ return error.UnknownTargetEntryPoint;
+ },
+ .enabled => target_util.defaultEntrySymbolName(target, wasi_exec_model) orelse
+ return error.UnknownTargetEntryPoint,
+ .named => |name| name,
+ };
+ if (entry != null and options.output_mode != .Exe)
+ return error.NonExecutableEntryPoint;
+
+ // *If* the LLVM backend were to be selected, should Zig use the LLVM
+ // library to build the LLVM module?
+ const use_lib_llvm = b: {
+ if (!build_options.have_llvm) {
+ if (options.use_lib_llvm == true) return error.LlvmLibraryUnavailable;
+ break :b false;
+ }
+ break :b options.use_lib_llvm orelse true;
+ };
+
+ const root_optimize_mode = options.root_optimize_mode orelse .Debug;
+
+ // Make a decision on whether to use LLVM backend for machine code generation.
+ // Note that using the LLVM backend does not necessarily mean using LLVM libraries.
+ // For example, Zig can emit .bc and .ll files directly, and this is still considered
+ // using "the LLVM backend".
+ const use_llvm = b: {
+ // If emitting to LLVM bitcode object format, must use LLVM backend.
+ if (options.emit_llvm_ir or options.emit_llvm_bc) {
+ if (options.use_llvm == false) return error.EmittingLlvmModuleRequiresLlvmBackend;
+ break :b true;
+ }
+
+ // If LLVM does not support the target, then we can't use it.
+ if (!target_util.hasLlvmSupport(target, target.ofmt)) {
+ if (options.use_llvm == true) return error.LlvmLacksTargetSupport;
+ break :b false;
+ }
+
+ if (options.use_llvm) |x| break :b x;
+
+ // If we have no zig code to compile, no need for LLVM.
+ if (!options.have_zcu) break :b false;
+
+ // If we cannot use LLVM libraries, then our own backends will be a
+ // better default since the LLVM backend can only produce bitcode
+ // and not an object file or executable.
+ if (!use_lib_llvm) break :b false;
+
+ // Prefer LLVM for release builds.
+ if (root_optimize_mode != .Debug) break :b true;
+
+ // At this point we would prefer to use our own self-hosted backend,
+ // because the compilation speed is better than LLVM. But only do it if
+ // we are confident in the robustness of the backend.
+ break :b !target_util.selfHostedBackendIsAsRobustAsLlvm(target);
+ };
+
+ if (!use_lib_llvm and use_llvm and options.emit_bin) {
+ // Explicit request to use LLVM to produce an object file, but without
+ // using LLVM libraries. Impossible.
+ return error.EmittingBinaryRequiresLlvmLibrary;
+ }
+
+ // Make a decision on whether to use LLD or our own linker.
+ const use_lld = b: {
+ if (target.isDarwin()) {
+ if (options.use_lld == true) return error.LldIncompatibleOs;
+ break :b false;
+ }
+
+ if (!build_options.have_llvm) {
+ if (options.use_lld == true) return error.LldUnavailable;
+ break :b false;
+ }
+
+ if (target.ofmt == .c) {
+ if (options.use_lld == true) return error.LldIncompatibleObjectFormat;
+ break :b false;
+ }
+
+ if (options.lto == true) {
+ if (options.use_lld == false) return error.LtoRequiresLld;
+ break :b true;
+ }
+
+ if (options.use_lld) |x| break :b x;
+ break :b true;
+ };
+
+ // Make a decision on whether to use Clang or Aro for translate-c and compiling C files.
+ const c_frontend: CFrontend = b: {
+ if (!build_options.have_llvm) {
+ if (options.use_clang == true) return error.ClangUnavailable;
+ break :b .aro;
+ }
+ if (options.use_clang) |clang| {
+ break :b if (clang) .clang else .aro;
+ }
+ break :b .clang;
+ };
+
+ const lto = b: {
+ if (!use_lld) {
+ // zig ld LTO support is tracked by
+ // https://github.com/ziglang/zig/issues/8680
+ if (options.lto == true) return error.LtoRequiresLld;
+ break :b false;
+ }
+
+ if (options.lto) |x| break :b x;
+ if (options.c_source_files_len == 0) break :b false;
+
+ if (target.cpu.arch.isRISCV()) {
+ // Clang and LLVM currently don't support RISC-V target-abi for LTO.
+ // Compiling with LTO may fail or produce undesired results.
+ // See https://reviews.llvm.org/D71387
+ // See https://reviews.llvm.org/D102582
+ break :b false;
+ }
+
+ break :b switch (options.output_mode) {
+ .Lib, .Obj => false,
+ .Exe => switch (root_optimize_mode) {
+ .Debug => false,
+ .ReleaseSafe, .ReleaseFast, .ReleaseSmall => true,
+ },
+ };
+ };
+
+ const link_libcpp = b: {
+ if (options.link_libcpp == true) break :b true;
+ if (options.any_sanitize_thread) {
+ // TSAN is (for now...) implemented in C++ so it requires linking libc++.
+ if (options.link_libcpp == false) return error.SanitizeThreadRequiresLibCpp;
+ break :b true;
+ }
+ if (options.ensure_libcpp_on_non_freestanding and target.os.tag != .freestanding)
+ break :b true;
+
+ break :b false;
+ };
+
+ const link_libunwind = b: {
+ if (link_libcpp and target_util.libcNeedsLibUnwind(target)) {
+ if (options.link_libunwind == false) return error.LibCppRequiresLibUnwind;
+ break :b true;
+ }
+ break :b options.link_libunwind orelse false;
+ };
+
+ const link_libc = b: {
+ if (target_util.osRequiresLibC(target)) {
+ if (options.link_libc == false) return error.OsRequiresLibC;
+ break :b true;
+ }
+ if (link_libcpp) {
+ if (options.link_libc == false) return error.LibCppRequiresLibC;
+ break :b true;
+ }
+ if (link_libunwind) {
+ if (options.link_libc == false) return error.LibUnwindRequiresLibC;
+ break :b true;
+ }
+ if (options.link_libc) |x| break :b x;
+ if (options.ensure_libc_on_non_freestanding and target.os.tag != .freestanding)
+ break :b true;
+
+ break :b false;
+ };
+
+ const any_unwind_tables = options.any_unwind_tables or
+ link_libunwind or target_util.needUnwindTables(target);
+
+ const link_mode = b: {
+ const explicitly_exe_or_dyn_lib = switch (options.output_mode) {
+ .Obj => false,
+ .Lib => (options.link_mode orelse .Static) == .Dynamic,
+ .Exe => true,
+ };
+
+ if (target_util.cannotDynamicLink(target)) {
+ if (options.link_mode == .Dynamic) return error.TargetCannotDynamicLink;
+ break :b .Static;
+ }
+ if (explicitly_exe_or_dyn_lib and link_libc and
+ (target.isGnuLibC() or target_util.osRequiresLibC(target)))
+ {
+ if (options.link_mode == .Static) return error.LibCRequiresDynamicLinking;
+ break :b .Dynamic;
+ }
+ // When creating a executable that links to system libraries, we
+ // require dynamic linking, but we must not link static libraries
+ // or object files dynamically!
+ if (options.any_dyn_libs and options.output_mode == .Exe) {
+ if (options.link_mode == .Static) return error.SharedLibrariesRequireDynamicLinking;
+ break :b .Dynamic;
+ }
+
+ if (options.link_mode) |link_mode| break :b link_mode;
+
+ if (explicitly_exe_or_dyn_lib and link_libc and
+ options.resolved_target.is_native_abi and target.abi.isMusl())
+ {
+ // If targeting the system's native ABI and the system's libc is
+ // musl, link dynamically by default.
+ break :b .Dynamic;
+ }
+
+ // Static is generally a better default. Fight me.
+ break :b .Static;
+ };
+
+ const import_memory = options.import_memory orelse false;
+ const export_memory = b: {
+ if (link_mode == .Dynamic) {
+ if (options.export_memory == true) return error.ExportMemoryAndDynamicIncompatible;
+ break :b false;
+ }
+ if (options.export_memory) |x| break :b x;
+ break :b !import_memory;
+ };
+
+ const pie: bool = b: {
+ switch (options.output_mode) {
+ .Obj, .Exe => {},
+ .Lib => if (link_mode == .Dynamic) {
+ if (options.pie == true) return error.DynamicLibraryPrecludesPie;
+ break :b false;
+ },
+ }
+ if (target_util.requiresPIE(target)) {
+ if (options.pie == false) return error.TargetRequiresPie;
+ break :b true;
+ }
+ if (options.any_sanitize_thread) {
+ if (options.pie == false) return error.SanitizeThreadRequiresPie;
+ break :b true;
+ }
+ if (options.pie) |pie| break :b pie;
+ break :b false;
+ };
+
+ return .{
+ .output_mode = options.output_mode,
+ .have_zcu = options.have_zcu,
+ .is_test = options.is_test,
+ .test_evented_io = options.test_evented_io,
+ .link_mode = link_mode,
+ .link_libc = link_libc,
+ .link_libcpp = link_libcpp,
+ .link_libunwind = link_libunwind,
+ .any_unwind_tables = any_unwind_tables,
+ .pie = pie,
+ .lto = lto,
+ .import_memory = import_memory,
+ .export_memory = export_memory,
+ .shared_memory = shared_memory,
+ .c_frontend = c_frontend,
+ .use_llvm = use_llvm,
+ .use_lib_llvm = use_lib_llvm,
+ .use_lld = use_lld,
+ .entry = entry,
+ .wasi_exec_model = wasi_exec_model,
+ };
+}
+
+const std = @import("std");
+const Module = @import("../Package.zig").Module;
+const Config = @This();
+const target_util = @import("../target.zig");
+const build_options = @import("build_options");
src/link/Coff.zig
@@ -48,9 +48,6 @@ got_table_count_dirty: bool = true,
got_table_contents_dirty: bool = true,
imports_count_dirty: bool = true,
-/// Virtual address of the entry point procedure relative to image base.
-entry_addr: ?u32 = null,
-
/// Table of tracked LazySymbols.
lazy_syms: LazySymbolTable = .{},
@@ -226,44 +223,150 @@ const ideal_factor = 3;
const minimum_text_block_size = 64;
pub const min_text_capacity = padToIdeal(minimum_text_block_size);
-pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Coff {
- assert(options.target.ofmt == .coff);
+pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Coff {
+ if (build_options.only_c) unreachable;
+ const target = options.comp.root_mod.resolved_target.result;
+ assert(target.ofmt == .coff);
+
+ const self = try createEmpty(arena, options);
+ errdefer self.base.destroy();
+
+ const use_lld = build_options.have_llvm and options.comp.config.use_lld;
+ const use_llvm = build_options.have_llvm and options.comp.config.use_llvm;
- if (options.use_llvm) {
- return createEmpty(allocator, options);
+ if (use_lld and use_llvm) {
+ // LLVM emits the object file; LLD links it into the final product.
+ return self;
}
- const self = try createEmpty(allocator, options);
- errdefer self.base.destroy();
+ const sub_path = if (!use_lld) options.emit.sub_path else p: {
+ // Open a temporary object file, not the final output file because we
+ // want to link with LLD.
+ const o_file_path = try std.fmt.allocPrint(arena, "{s}{s}", .{
+ options.emit.sub_path, target.ofmt.fileExt(target.cpu.arch),
+ });
+ self.base.intermediary_basename = o_file_path;
+ break :p o_file_path;
+ };
- const file = try options.emit.?.directory.handle.createFile(sub_path, .{
+ self.base.file = try options.emit.directory.handle.createFile(sub_path, .{
.truncate = false,
.read = true,
- .mode = link.determineMode(options),
+ .mode = link.File.determineMode(
+ use_lld,
+ options.comp.config.output_mode,
+ options.comp.config.link_mode,
+ ),
});
- self.base.file = file;
- try self.populateMissingMetadata();
+ assert(self.llvm_object == null);
+ const gpa = self.base.comp.gpa;
+
+ try self.strtab.buffer.ensureUnusedCapacity(gpa, @sizeOf(u32));
+ self.strtab.buffer.appendNTimesAssumeCapacity(0, @sizeOf(u32));
+
+ try self.temp_strtab.buffer.append(gpa, 0);
+
+ // Index 0 is always a null symbol.
+ try self.locals.append(gpa, .{
+ .name = [_]u8{0} ** 8,
+ .value = 0,
+ .section_number = .UNDEFINED,
+ .type = .{ .base_type = .NULL, .complex_type = .NULL },
+ .storage_class = .NULL,
+ .number_of_aux_symbols = 0,
+ });
+
+ if (self.text_section_index == null) {
+ const file_size: u32 = @intCast(options.program_code_size_hint);
+ self.text_section_index = try self.allocateSection(".text", file_size, .{
+ .CNT_CODE = 1,
+ .MEM_EXECUTE = 1,
+ .MEM_READ = 1,
+ });
+ }
+
+ if (self.got_section_index == null) {
+ const file_size = @as(u32, @intCast(options.symbol_count_hint)) * self.ptr_width.size();
+ self.got_section_index = try self.allocateSection(".got", file_size, .{
+ .CNT_INITIALIZED_DATA = 1,
+ .MEM_READ = 1,
+ });
+ }
+
+ if (self.rdata_section_index == null) {
+ const file_size: u32 = self.page_size;
+ self.rdata_section_index = try self.allocateSection(".rdata", file_size, .{
+ .CNT_INITIALIZED_DATA = 1,
+ .MEM_READ = 1,
+ });
+ }
+
+ if (self.data_section_index == null) {
+ const file_size: u32 = self.page_size;
+ self.data_section_index = try self.allocateSection(".data", file_size, .{
+ .CNT_INITIALIZED_DATA = 1,
+ .MEM_READ = 1,
+ .MEM_WRITE = 1,
+ });
+ }
+
+ if (self.idata_section_index == null) {
+ const file_size = @as(u32, @intCast(options.symbol_count_hint)) * self.ptr_width.size();
+ self.idata_section_index = try self.allocateSection(".idata", file_size, .{
+ .CNT_INITIALIZED_DATA = 1,
+ .MEM_READ = 1,
+ });
+ }
+
+ if (self.reloc_section_index == null) {
+ const file_size = @as(u32, @intCast(options.symbol_count_hint)) * @sizeOf(coff.BaseRelocation);
+ self.reloc_section_index = try self.allocateSection(".reloc", file_size, .{
+ .CNT_INITIALIZED_DATA = 1,
+ .MEM_DISCARDABLE = 1,
+ .MEM_READ = 1,
+ });
+ }
+
+ if (self.strtab_offset == null) {
+ const file_size = @as(u32, @intCast(self.strtab.buffer.items.len));
+ self.strtab_offset = self.findFreeSpace(file_size, @alignOf(u32)); // 4bytes aligned seems like a good idea here
+ log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + file_size });
+ }
+
+ {
+ // We need to find out what the max file offset is according to section headers.
+ // Otherwise, we may end up with an COFF binary with file size not matching the final section's
+ // offset + it's filesize.
+ // TODO I don't like this here one bit
+ var max_file_offset: u64 = 0;
+ for (self.sections.items(.header)) |header| {
+ if (header.pointer_to_raw_data + header.size_of_raw_data > max_file_offset) {
+ max_file_offset = header.pointer_to_raw_data + header.size_of_raw_data;
+ }
+ }
+ try self.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset);
+ }
return self;
}
-pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
- const ptr_width: PtrWidth = switch (options.target.ptrBitWidth()) {
+pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Coff {
+ const target = options.comp.root_mod.resolved_target.result;
+ const ptr_width: PtrWidth = switch (target.ptrBitWidth()) {
0...32 => .p32,
33...64 => .p64,
else => return error.UnsupportedCOFFArchitecture,
};
- const page_size: u32 = switch (options.target.cpu.arch) {
+ const page_size: u32 = switch (target.cpu.arch) {
else => 0x1000,
};
- const self = try gpa.create(Coff);
- errdefer gpa.destroy(self);
+ const self = try arena.create(Coff);
self.* = .{
.base = .{
.tag = .coff,
- .options = options,
- .allocator = gpa,
+ .comp = options.comp,
+ .emit = options.emit,
.file = null,
},
.ptr_width = ptr_width,
@@ -271,16 +374,17 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
.data_directories = comptime mem.zeroes([coff.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]coff.ImageDataDirectory),
};
- if (options.use_llvm) {
- self.llvm_object = try LlvmObject.create(gpa, options);
+ const use_llvm = build_options.have_llvm and options.comp.config.use_llvm;
+ if (use_llvm and options.comp.config.have_zcu) {
+ self.llvm_object = try LlvmObject.create(arena, options);
}
return self;
}
pub fn deinit(self: *Coff) void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
- if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);
+ if (self.llvm_object) |llvm_object| llvm_object.deinit();
for (self.objects.items) |*object| {
object.deinit(gpa);
@@ -349,97 +453,6 @@ pub fn deinit(self: *Coff) void {
self.base_relocs.deinit(gpa);
}
-fn populateMissingMetadata(self: *Coff) !void {
- assert(self.llvm_object == null);
- const gpa = self.base.allocator;
-
- try self.strtab.buffer.ensureUnusedCapacity(gpa, @sizeOf(u32));
- self.strtab.buffer.appendNTimesAssumeCapacity(0, @sizeOf(u32));
-
- try self.temp_strtab.buffer.append(gpa, 0);
-
- // Index 0 is always a null symbol.
- try self.locals.append(gpa, .{
- .name = [_]u8{0} ** 8,
- .value = 0,
- .section_number = .UNDEFINED,
- .type = .{ .base_type = .NULL, .complex_type = .NULL },
- .storage_class = .NULL,
- .number_of_aux_symbols = 0,
- });
-
- if (self.text_section_index == null) {
- const file_size = @as(u32, @intCast(self.base.options.program_code_size_hint));
- self.text_section_index = try self.allocateSection(".text", file_size, .{
- .CNT_CODE = 1,
- .MEM_EXECUTE = 1,
- .MEM_READ = 1,
- });
- }
-
- if (self.got_section_index == null) {
- const file_size = @as(u32, @intCast(self.base.options.symbol_count_hint)) * self.ptr_width.size();
- self.got_section_index = try self.allocateSection(".got", file_size, .{
- .CNT_INITIALIZED_DATA = 1,
- .MEM_READ = 1,
- });
- }
-
- if (self.rdata_section_index == null) {
- const file_size: u32 = self.page_size;
- self.rdata_section_index = try self.allocateSection(".rdata", file_size, .{
- .CNT_INITIALIZED_DATA = 1,
- .MEM_READ = 1,
- });
- }
-
- if (self.data_section_index == null) {
- const file_size: u32 = self.page_size;
- self.data_section_index = try self.allocateSection(".data", file_size, .{
- .CNT_INITIALIZED_DATA = 1,
- .MEM_READ = 1,
- .MEM_WRITE = 1,
- });
- }
-
- if (self.idata_section_index == null) {
- const file_size = @as(u32, @intCast(self.base.options.symbol_count_hint)) * self.ptr_width.size();
- self.idata_section_index = try self.allocateSection(".idata", file_size, .{
- .CNT_INITIALIZED_DATA = 1,
- .MEM_READ = 1,
- });
- }
-
- if (self.reloc_section_index == null) {
- const file_size = @as(u32, @intCast(self.base.options.symbol_count_hint)) * @sizeOf(coff.BaseRelocation);
- self.reloc_section_index = try self.allocateSection(".reloc", file_size, .{
- .CNT_INITIALIZED_DATA = 1,
- .MEM_DISCARDABLE = 1,
- .MEM_READ = 1,
- });
- }
-
- if (self.strtab_offset == null) {
- const file_size = @as(u32, @intCast(self.strtab.buffer.items.len));
- self.strtab_offset = self.findFreeSpace(file_size, @alignOf(u32)); // 4bytes aligned seems like a good idea here
- log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + file_size });
- }
-
- {
- // We need to find out what the max file offset is according to section headers.
- // Otherwise, we may end up with an COFF binary with file size not matching the final section's
- // offset + it's filesize.
- // TODO I don't like this here one bit
- var max_file_offset: u64 = 0;
- for (self.sections.items(.header)) |header| {
- if (header.pointer_to_raw_data + header.size_of_raw_data > max_file_offset) {
- max_file_offset = header.pointer_to_raw_data + header.size_of_raw_data;
- }
- }
- try self.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset);
- }
-}
-
fn allocateSection(self: *Coff, name: []const u8, size: u32, flags: coff.SectionHeaderFlags) !u16 {
const index = @as(u16, @intCast(self.sections.slice().len));
const off = self.findFreeSpace(size, default_file_alignment);
@@ -471,8 +484,9 @@ fn allocateSection(self: *Coff, name: []const u8, size: u32, flags: coff.Section
.number_of_linenumbers = 0,
.flags = flags,
};
+ const gpa = self.base.comp.gpa;
try self.setSectionName(&header, name);
- try self.sections.append(self.base.allocator, .{ .header = header });
+ try self.sections.append(gpa, .{ .header = header });
return index;
}
@@ -654,7 +668,7 @@ fn allocateAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignme
}
pub fn allocateSymbol(self: *Coff) !u32 {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
try self.locals.ensureUnusedCapacity(gpa, 1);
const index = blk: {
@@ -682,7 +696,7 @@ pub fn allocateSymbol(self: *Coff) !u32 {
}
fn allocateGlobal(self: *Coff) !u32 {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
try self.globals.ensureUnusedCapacity(gpa, 1);
const index = blk: {
@@ -706,15 +720,16 @@ fn allocateGlobal(self: *Coff) !u32 {
}
fn addGotEntry(self: *Coff, target: SymbolWithLoc) !void {
+ const gpa = self.base.comp.gpa;
if (self.got_table.lookup.contains(target)) return;
- const got_index = try self.got_table.allocateEntry(self.base.allocator, target);
+ const got_index = try self.got_table.allocateEntry(gpa, target);
try self.writeOffsetTableEntry(got_index);
self.got_table_count_dirty = true;
self.markRelocsDirtyByTarget(target);
}
pub fn createAtom(self: *Coff) !Atom.Index {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const atom_index = @as(Atom.Index, @intCast(self.atoms.items.len));
const atom = try self.atoms.addOne(gpa);
const sym_index = try self.allocateSymbol();
@@ -759,7 +774,7 @@ fn writeAtom(self: *Coff, atom_index: Atom.Index, code: []u8) !void {
file_offset + code.len,
});
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
// Gather relocs which can be resolved.
// We need to do this as we will be applying different slide values depending
@@ -870,7 +885,7 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
if (is_hot_update_compatible) {
if (self.base.child_pid) |handle| {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const slide = @intFromPtr(self.hot_state.loaded_base_address.?);
const actual_vmaddr = vmaddr + slide;
const pvaddr = @as(*anyopaque, @ptrFromInt(actual_vmaddr));
@@ -974,7 +989,7 @@ pub fn ptraceDetach(self: *Coff, handle: std.ChildProcess.Id) void {
fn freeAtom(self: *Coff, atom_index: Atom.Index) void {
log.debug("freeAtom {d}", .{atom_index});
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
// Remove any relocs and base relocs associated with this Atom
Atom.freeRelocations(self, atom_index);
@@ -1061,7 +1076,8 @@ pub fn updateFunc(self: *Coff, mod: *Module, func_index: InternPool.Index, air:
self.freeUnnamedConsts(decl_index);
Atom.freeRelocations(self, atom_index);
- var code_buffer = std.ArrayList(u8).init(self.base.allocator);
+ const gpa = self.base.comp.gpa;
+ var code_buffer = std.ArrayList(u8).init(gpa);
defer code_buffer.deinit();
const res = try codegen.generateFunction(
@@ -1090,7 +1106,7 @@ pub fn updateFunc(self: *Coff, mod: *Module, func_index: InternPool.Index, air:
}
pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: InternPool.DeclIndex) !u32 {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const mod = self.base.options.module.?;
const decl = mod.declPtr(decl_index);
const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);
@@ -1121,7 +1137,7 @@ const LowerConstResult = union(enum) {
};
fn lowerConst(self: *Coff, name: []const u8, tv: TypedValue, required_alignment: InternPool.Alignment, sect_id: u16, src_loc: Module.SrcLoc) !LowerConstResult {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var code_buffer = std.ArrayList(u8).init(gpa);
defer code_buffer.deinit();
@@ -1174,13 +1190,14 @@ pub fn updateDecl(
return;
}
+ const gpa = self.base.comp.gpa;
if (decl.isExtern(mod)) {
// TODO make this part of getGlobalSymbol
const variable = decl.getOwnedVariable(mod).?;
const name = mod.intern_pool.stringToSlice(decl.name);
const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name);
const global_index = try self.getGlobalSymbol(name, lib_name);
- try self.need_got_table.put(self.base.allocator, global_index, {});
+ try self.need_got_table.put(gpa, global_index, {});
return;
}
@@ -1188,7 +1205,7 @@ pub fn updateDecl(
Atom.freeRelocations(self, atom_index);
const atom = self.getAtom(atom_index);
- var code_buffer = std.ArrayList(u8).init(self.base.allocator);
+ var code_buffer = std.ArrayList(u8).init(gpa);
defer code_buffer.deinit();
const decl_val = if (decl.val.getVariable(mod)) |variable| Value.fromInterned(variable.init) else decl.val;
@@ -1220,7 +1237,7 @@ fn updateLazySymbolAtom(
atom_index: Atom.Index,
section_index: u16,
) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const mod = self.base.options.module.?;
var required_alignment: InternPool.Alignment = .none;
@@ -1281,8 +1298,9 @@ fn updateLazySymbolAtom(
}
pub fn getOrCreateAtomForLazySymbol(self: *Coff, sym: link.File.LazySymbol) !Atom.Index {
+ const gpa = self.base.comp.gpa;
const mod = self.base.options.module.?;
- const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(mod));
+ const gop = try self.lazy_syms.getOrPut(gpa, sym.getDecl(mod));
errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
if (!gop.found_existing) gop.value_ptr.* = .{};
const metadata: struct { atom: *Atom.Index, state: *LazySymbolMetadata.State } = switch (sym.kind) {
@@ -1305,7 +1323,8 @@ pub fn getOrCreateAtomForLazySymbol(self: *Coff, sym: link.File.LazySymbol) !Ato
}
pub fn getOrCreateAtomForDecl(self: *Coff, decl_index: InternPool.DeclIndex) !Atom.Index {
- const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
+ const gpa = self.base.comp.gpa;
+ const gop = try self.decls.getOrPut(gpa, decl_index);
if (!gop.found_existing) {
gop.value_ptr.* = .{
.atom = try self.createAtom(),
@@ -1401,7 +1420,7 @@ fn updateDeclCode(self: *Coff, decl_index: InternPool.DeclIndex, code: []u8, com
}
fn freeUnnamedConsts(self: *Coff, decl_index: InternPool.DeclIndex) void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
for (unnamed_consts.items) |atom_index| {
self.freeAtom(atom_index);
@@ -1412,6 +1431,7 @@ fn freeUnnamedConsts(self: *Coff, decl_index: InternPool.DeclIndex) void {
pub fn freeDecl(self: *Coff, decl_index: InternPool.DeclIndex) void {
if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
+ const gpa = self.base.comp.gpa;
const mod = self.base.options.module.?;
const decl = mod.declPtr(decl_index);
@@ -1421,7 +1441,7 @@ pub fn freeDecl(self: *Coff, decl_index: InternPool.DeclIndex) void {
var kv = const_kv;
self.freeAtom(kv.value.atom);
self.freeUnnamedConsts(decl_index);
- kv.value.exports.deinit(self.base.allocator);
+ kv.value.exports.deinit(gpa);
}
}
@@ -1476,7 +1496,7 @@ pub fn updateExports(
if (self.base.options.emit == null) return;
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const metadata = switch (exported) {
.decl_index => |decl_index| blk: {
@@ -1574,7 +1594,7 @@ pub fn deleteDeclExport(
const name = mod.intern_pool.stringToSlice(name_ip);
const sym_index = metadata.getExportPtr(self, name) orelse return;
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const sym_loc = SymbolWithLoc{ .sym_index = sym_index.*, .file = null };
const sym = self.getSymbolPtr(sym_loc);
log.debug("deleting export '{s}'", .{name});
@@ -1602,7 +1622,7 @@ pub fn deleteDeclExport(
}
fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const sym = self.getSymbol(current);
const sym_name = self.getSymbolName(current);
@@ -1653,7 +1673,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
sub_prog_node.activate();
defer sub_prog_node.end();
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
@@ -1794,7 +1814,7 @@ pub fn lowerAnonDecl(
explicit_alignment: InternPool.Alignment,
src_loc: Module.SrcLoc,
) !codegen.Result {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const mod = self.base.options.module.?;
const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val));
const decl_alignment = switch (explicit_alignment) {
@@ -1868,7 +1888,7 @@ pub fn getGlobalSymbol(self: *Coff, name: []const u8, lib_name_name: ?[]const u8
const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
gop.value_ptr.* = sym_loc;
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const sym = self.getSymbolPtr(sym_loc);
try self.setSymbolName(sym, name);
sym.storage_class = .EXTERNAL;
@@ -1895,7 +1915,7 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl_index: InternPool
/// TODO: note that .ABSOLUTE is used as padding within each block; we could use this fact to do
/// incremental updates and writes into the table instead of doing it all at once
fn writeBaseRelocations(self: *Coff) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var page_table = std.AutoHashMap(u32, std.ArrayList(coff.BaseRelocation)).init(gpa);
defer {
@@ -2006,7 +2026,7 @@ fn writeImportTables(self: *Coff) !void {
if (self.idata_section_index == null) return;
if (!self.imports_count_dirty) return;
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const ext = ".dll";
const header = &self.sections.items(.header)[self.idata_section_index.?];
@@ -2154,7 +2174,8 @@ fn writeStrtab(self: *Coff) !void {
log.debug("writing strtab from 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + needed_size });
- var buffer = std.ArrayList(u8).init(self.base.allocator);
+ const gpa = self.base.comp.gpa;
+ var buffer = std.ArrayList(u8).init(gpa);
defer buffer.deinit();
try buffer.ensureTotalCapacityPrecise(needed_size);
buffer.appendSliceAssumeCapacity(self.strtab.buffer.items);
@@ -2176,7 +2197,7 @@ fn writeDataDirectoriesHeaders(self: *Coff) !void {
}
fn writeHeader(self: *Coff) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var buffer = std.ArrayList(u8).init(gpa);
defer buffer.deinit();
const writer = buffer.writer();
@@ -2499,7 +2520,7 @@ pub fn getOrPutGlobalPtr(self: *Coff, name: []const u8) !GetOrPutGlobalPtrResult
if (self.getGlobalPtr(name)) |ptr| {
return GetOrPutGlobalPtrResult{ .found_existing = true, .value_ptr = ptr };
}
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const global_index = try self.allocateGlobal();
const global_name = try gpa.dupe(u8, name);
_ = try self.resolver.put(gpa, global_name, global_index);
@@ -2530,7 +2551,8 @@ fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !v
@memset(header.name[name.len..], 0);
return;
}
- const offset = try self.strtab.insert(self.base.allocator, name);
+ const gpa = self.base.comp.gpa;
+ const offset = try self.strtab.insert(gpa, name);
const name_offset = fmt.bufPrint(&header.name, "/{d}", .{offset}) catch unreachable;
@memset(header.name[name_offset.len..], 0);
}
@@ -2549,7 +2571,8 @@ fn setSymbolName(self: *Coff, symbol: *coff.Symbol, name: []const u8) !void {
@memset(symbol.name[name.len..], 0);
return;
}
- const offset = try self.strtab.insert(self.base.allocator, name);
+ const gpa = self.base.comp.gpa;
+ const offset = try self.strtab.insert(gpa, name);
@memset(symbol.name[0..4], 0);
mem.writeInt(u32, symbol.name[4..8], offset, .little);
}
src/link/Elf.zig
@@ -200,26 +200,34 @@ pub const min_text_capacity = padToIdeal(minimum_atom_size);
pub const PtrWidth = enum { p32, p64 };
-pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf {
- assert(options.target.ofmt == .elf);
+pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Elf {
+ if (build_options.only_c) unreachable;
+ const target = options.comp.root_mod.resolved_target.result;
+ assert(target.ofmt == .elf);
- const self = try createEmpty(allocator, options);
+ const use_lld = build_options.have_llvm and options.comp.config.use_lld;
+ const use_llvm = build_options.have_llvm and options.comp.config.use_llvm;
+
+ const self = try createEmpty(arena, options);
errdefer self.base.destroy();
+ if (use_lld and use_llvm) {
+ // LLVM emits the object file; LLD links it into the final product.
+ return self;
+ }
+
const is_obj = options.output_mode == .Obj;
const is_obj_or_ar = is_obj or (options.output_mode == .Lib and options.link_mode == .Static);
- if (options.use_llvm) {
- const use_lld = build_options.have_llvm and self.base.options.use_lld;
- if (use_lld) return self;
-
- if (options.module != null) {
- self.base.intermediary_basename = try std.fmt.allocPrint(allocator, "{s}{s}", .{
- sub_path, options.target.ofmt.fileExt(options.target.cpu.arch),
- });
- }
- }
- errdefer if (self.base.intermediary_basename) |path| allocator.free(path);
+ const sub_path = if (!use_lld) options.emit.sub_path else p: {
+ // Open a temporary object file, not the final output file because we
+ // want to link with LLD.
+ const o_file_path = try std.fmt.allocPrint(arena, "{s}{s}", .{
+ options.emit.sub_path, target.ofmt.fileExt(target.cpu.arch),
+ });
+ self.base.intermediary_basename = o_file_path;
+ break :p o_file_path;
+ };
self.base.file = try options.emit.?.directory.handle.createFile(sub_path, .{
.truncate = false,
@@ -227,24 +235,26 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
.mode = link.determineMode(options),
});
+ const gpa = options.comp.gpa;
+
// Index 0 is always a null symbol.
- try self.symbols.append(allocator, .{});
+ try self.symbols.append(gpa, .{});
// Index 0 is always a null symbol.
- try self.symbols_extra.append(allocator, 0);
+ try self.symbols_extra.append(gpa, 0);
// Allocate atom index 0 to null atom
- try self.atoms.append(allocator, .{});
+ try self.atoms.append(gpa, .{});
// Append null file at index 0
- try self.files.append(allocator, .null);
+ try self.files.append(gpa, .null);
// Append null byte to string tables
- try self.shstrtab.append(allocator, 0);
- try self.strtab.append(allocator, 0);
+ try self.shstrtab.append(gpa, 0);
+ try self.strtab.append(gpa, 0);
// There must always be a null shdr in index 0
_ = try self.addSection(.{ .name = "" });
// Append null symbol in output symtab
- try self.symtab.append(allocator, null_sym);
+ try self.symtab.append(gpa, null_sym);
if (!is_obj_or_ar) {
- try self.dynstrtab.append(allocator, 0);
+ try self.dynstrtab.append(gpa, 0);
// Initialize PT_PHDR program header
const p_align: u16 = switch (self.ptr_width) {
@@ -283,10 +293,10 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
}
if (options.module != null and !options.use_llvm) {
- const index = @as(File.Index, @intCast(try self.files.addOne(allocator)));
+ const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
self.files.set(index, .{ .zig_object = .{
.index = index,
- .path = try std.fmt.allocPrint(self.base.allocator, "{s}.o", .{std.fs.path.stem(
+ .path = try std.fmt.allocPrint(arena, "{s}.o", .{std.fs.path.stem(
options.module.?.main_mod.root_src_path,
)}),
} });
@@ -298,16 +308,16 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
return self;
}
-pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
- const ptr_width: PtrWidth = switch (options.target.ptrBitWidth()) {
+pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Elf {
+ const target = options.comp.root_mod.resolved_target.result;
+ const ptr_width: PtrWidth = switch (target.ptrBitWidth()) {
0...32 => .p32,
33...64 => .p64,
else => return error.UnsupportedELFArchitecture,
};
- const self = try gpa.create(Elf);
- errdefer gpa.destroy(self);
+ const self = try arena.create(Elf);
- const page_size: u32 = switch (options.target.cpu.arch) {
+ const page_size: u32 = switch (target.cpu.arch) {
.powerpc64le => 0x10000,
.sparc64 => 0x2000,
else => 0x1000,
@@ -321,25 +331,25 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
self.* = .{
.base = .{
.tag = .elf,
- .options = options,
- .allocator = gpa,
+ .comp = options.comp,
+ .emit = options.emit,
.file = null,
},
.ptr_width = ptr_width,
.page_size = page_size,
.default_sym_version = default_sym_version,
};
- if (options.use_llvm and options.module != null) {
- self.llvm_object = try LlvmObject.create(gpa, options);
+ if (options.use_llvm and options.comp.config.have_zcu) {
+ self.llvm_object = try LlvmObject.create(arena, options);
}
return self;
}
pub fn deinit(self: *Elf) void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
- if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);
+ if (self.llvm_object) |llvm_object| llvm_object.deinit();
for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) {
.null => {},
@@ -496,10 +506,11 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {
/// TODO move to ZigObject
pub fn initMetadata(self: *Elf) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const ptr_size = self.ptrWidthBytes();
- const ptr_bit_width = self.base.options.target.ptrBitWidth();
- const is_linux = self.base.options.target.os.tag == .linux;
+ const target = self.base.comp.root_mod.resolved_target.result;
+ const ptr_bit_width = target.ptrBitWidth();
+ const is_linux = target.os.tag == .linux;
const zig_object = self.zigObjectPtr().?;
const fillSection = struct {
@@ -943,7 +954,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
if (use_lld) return;
}
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var sub_prog_node = prog_node.start("ELF Flush", 0);
sub_prog_node.activate();
defer sub_prog_node.end();
@@ -952,7 +963,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
defer arena_allocator.deinit();
const arena = arena_allocator.allocator();
- const target = self.base.options.target;
+ const target = self.base.comp.root_mod.resolved_target.result;
const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
const module_obj_path: ?[]const u8 = if (self.base.intermediary_basename) |path| blk: {
@@ -1303,7 +1314,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
}
pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
defer positionals.deinit();
@@ -1447,7 +1458,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
}
pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
defer positionals.deinit();
@@ -1524,7 +1535,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
defer arena_allocator.deinit();
const arena = arena_allocator.allocator();
- const target = self.base.options.target;
+ const target = self.base.comp.root_mod.resolved_target.result;
const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
const module_obj_path: ?[]const u8 = if (self.base.intermediary_basename) |path| blk: {
@@ -1574,7 +1585,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
}
} else {
if (!self.isStatic()) {
- if (self.base.options.target.dynamic_linker.get()) |path| {
+ if (target.dynamic_linker.get()) |path| {
try argv.append("-dynamic-linker");
try argv.append(path);
}
@@ -1842,7 +1853,7 @@ fn parseObject(self: *Elf, path: []const u8) ParseError!void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const in_file = try std.fs.cwd().openFile(path, .{});
defer in_file.close();
const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
@@ -1862,7 +1873,7 @@ fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const in_file = try std.fs.cwd().openFile(path, .{});
defer in_file.close();
const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
@@ -1888,7 +1899,7 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const in_file = try std.fs.cwd().openFile(lib.path, .{});
defer in_file.close();
const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
@@ -1910,7 +1921,7 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const in_file = try std.fs.cwd().openFile(lib.path, .{});
defer in_file.close();
const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
@@ -1996,7 +2007,7 @@ fn accessLibPath(
link_mode: ?std.builtin.LinkMode,
) !bool {
const sep = fs.path.sep_str;
- const target = self.base.options.target;
+ const target = self.base.comp.root_mod.resolved_target.result;
test_path.clearRetainingCapacity();
const prefix = if (link_mode != null) "lib" else "";
const suffix = if (link_mode) |mode| switch (mode) {
@@ -2190,7 +2201,7 @@ fn claimUnresolvedObject(self: *Elf) void {
/// This is also the point where we will report undefined symbols for any
/// alloc sections.
fn scanRelocs(self: *Elf) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Atom.Index)).init(gpa);
defer {
@@ -2293,7 +2304,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
const have_dynamic_linker = self.base.options.link_libc and
self.base.options.link_mode == .Dynamic and is_exe_or_dyn_lib;
- const target = self.base.options.target;
+ const target = self.base.comp.root_mod.resolved_target.result;
const gc_sections = self.base.options.gc_sections orelse !is_obj;
const stack_size = self.base.options.stack_size_override orelse 16777216;
const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;
@@ -2374,7 +2385,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
man.hash.addBytes(libc_installation.crt_dir.?);
}
if (have_dynamic_linker) {
- man.hash.addOptionalBytes(self.base.options.target.dynamic_linker.get());
+ man.hash.addOptionalBytes(target.dynamic_linker.get());
}
}
man.hash.addOptionalBytes(self.base.options.soname);
@@ -2687,7 +2698,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
}
if (have_dynamic_linker) {
- if (self.base.options.target.dynamic_linker.get()) |dynamic_linker| {
+ if (target.dynamic_linker.get()) |dynamic_linker| {
try argv.append("-dynamic-linker");
try argv.append(dynamic_linker);
}
@@ -2937,7 +2948,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
}
fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
- const target_endian = self.base.options.target.cpu.arch.endian();
+ const target = self.base.comp.root_mod.resolved_target.result;
+ const target_endian = target.cpu.arch.endian();
switch (self.ptr_width) {
.p32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @as(u32, @intCast(addr)), target_endian),
.p64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), addr, target_endian),
@@ -2945,8 +2957,9 @@ fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64)
}
fn writeShdrTable(self: *Elf) !void {
- const gpa = self.base.allocator;
- const target_endian = self.base.options.target.cpu.arch.endian();
+ const gpa = self.base.comp.gpa;
+ const target = self.base.comp.root_mod.resolved_target.result;
+ const target_endian = target.cpu.arch.endian();
const foreign_endian = target_endian != builtin.cpu.arch.endian();
const shsize: u64 = switch (self.ptr_width) {
.p32 => @sizeOf(elf.Elf32_Shdr),
@@ -3001,8 +3014,9 @@ fn writeShdrTable(self: *Elf) !void {
}
fn writePhdrTable(self: *Elf) !void {
- const gpa = self.base.allocator;
- const target_endian = self.base.options.target.cpu.arch.endian();
+ const gpa = self.base.comp.gpa;
+ const target = self.base.comp.root_mod.resolved_target.result;
+ const target_endian = target.cpu.arch.endian();
const foreign_endian = target_endian != builtin.cpu.arch.endian();
const phdr_table = &self.phdrs.items[self.phdr_table_index.?];
@@ -3054,7 +3068,8 @@ fn writeElfHeader(self: *Elf) !void {
};
index += 1;
- const endian = self.base.options.target.cpu.arch.endian();
+ const target = self.base.comp.root_mod.resolved_target.result;
+ const endian = target.cpu.arch.endian();
hdr_buf[index] = switch (endian) {
.little => elf.ELFDATA2LSB,
.big => elf.ELFDATA2MSB,
@@ -3083,7 +3098,7 @@ fn writeElfHeader(self: *Elf) !void {
mem.writeInt(u16, hdr_buf[index..][0..2], @intFromEnum(elf_type), endian);
index += 2;
- const machine = self.base.options.target.cpu.arch.toElfMachine();
+ const machine = target.cpu.arch.toElfMachine();
mem.writeInt(u16, hdr_buf[index..][0..2], @intFromEnum(machine), endian);
index += 2;
@@ -3248,7 +3263,7 @@ fn addLinkerDefinedSymbols(self: *Elf) !void {
for (self.shdrs.items) |shdr| {
if (self.getStartStopBasename(shdr)) |name| {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
try self.start_stop_indexes.ensureUnusedCapacity(gpa, 2);
const start = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name});
@@ -3394,6 +3409,7 @@ fn initOutputSections(self: *Elf) !void {
}
fn initSyntheticSections(self: *Elf) !void {
+ const target = self.base.comp.root_mod.resolved_target.result;
const ptr_size = self.ptrWidthBytes();
const needs_eh_frame = for (self.objects.items) |index| {
@@ -3503,7 +3519,7 @@ fn initSyntheticSections(self: *Elf) !void {
// a segfault in the dynamic linker trying to load a binary that is static
// and doesn't contain .dynamic section.
if (self.isStatic() and !self.base.options.pie) break :blk false;
- break :blk self.base.options.target.dynamic_linker.get() != null;
+ break :blk target.dynamic_linker.get() != null;
};
if (needs_interp) {
self.interp_section_index = try self.addSection(.{
@@ -3613,7 +3629,7 @@ fn initSectionsObject(self: *Elf) !void {
}
fn initComdatGroups(self: *Elf) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
for (self.objects.items) |index| {
const object = self.file(index).?.object;
@@ -3732,7 +3748,7 @@ fn initSpecialPhdrs(self: *Elf) !void {
/// Ties are broken by the file prority which corresponds to the inclusion of input sections in this output section
/// we are about to sort.
fn sortInitFini(self: *Elf) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const Entry = struct {
priority: i32,
@@ -3872,7 +3888,7 @@ fn sortPhdrs(self: *Elf) error{OutOfMemory}!void {
}
};
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var entries = try std.ArrayList(Entry).initCapacity(gpa, self.phdrs.items.len);
defer entries.deinit();
for (0..self.phdrs.items.len) |phndx| {
@@ -3977,7 +3993,7 @@ fn sortShdrs(self: *Elf) !void {
}
};
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var entries = try std.ArrayList(Entry).initCapacity(gpa, self.shdrs.items.len);
defer entries.deinit();
for (0..self.shdrs.items.len) |shndx| {
@@ -4004,7 +4020,7 @@ fn sortShdrs(self: *Elf) !void {
}
fn resetShdrIndexes(self: *Elf, backlinks: []const u16) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
for (&[_]*?u16{
&self.eh_frame_section_index,
@@ -4187,6 +4203,7 @@ fn resetShdrIndexes(self: *Elf, backlinks: []const u16) !void {
}
fn updateSectionSizes(self: *Elf) !void {
+ const target = self.base.comp.root_mod.resolved_target.result;
for (self.output_sections.keys(), self.output_sections.values()) |shndx, atom_list| {
const shdr = &self.shdrs.items[shndx];
for (atom_list.items) |atom_index| {
@@ -4244,7 +4261,7 @@ fn updateSectionSizes(self: *Elf) !void {
}
if (self.interp_section_index) |index| {
- self.shdrs.items[index].sh_size = self.base.options.target.dynamic_linker.get().?.len + 1;
+ self.shdrs.items[index].sh_size = target.dynamic_linker.get().?.len + 1;
}
if (self.hash_section_index) |index| {
@@ -4453,7 +4470,7 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {
// as we are more interested in quick turnaround and compatibility
// with `findFreeSpace` mechanics than anything else.
const Cover = std.ArrayList(u16);
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var covers: [max_number_of_object_segments]Cover = undefined;
for (&covers) |*cover| {
cover.* = Cover.init(gpa);
@@ -4691,7 +4708,7 @@ fn allocateAtoms(self: *Elf) void {
}
fn writeAtoms(self: *Elf) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Atom.Index)).init(gpa);
defer {
@@ -4779,7 +4796,7 @@ fn writeAtoms(self: *Elf) !void {
}
fn writeAtomsObject(self: *Elf) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
// TODO iterate over `output_sections` directly
for (self.shdrs.items, 0..) |shdr, shndx| {
@@ -4852,7 +4869,7 @@ fn updateSymtabSize(self: *Elf) !void {
var nglobals: u32 = 0;
var strsize: u32 = 0;
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var files = std.ArrayList(File.Index).init(gpa);
defer files.deinit();
try files.ensureTotalCapacityPrecise(self.objects.items.len + self.shared_objects.items.len + 2);
@@ -4935,11 +4952,12 @@ fn updateSymtabSize(self: *Elf) !void {
}
fn writeSyntheticSections(self: *Elf) !void {
- const gpa = self.base.allocator;
+ const target = self.base.comp.root_mod.resolved_target.result;
+ const gpa = self.base.comp.gpa;
if (self.interp_section_index) |shndx| {
var buffer: [256]u8 = undefined;
- const interp = self.base.options.target.dynamic_linker.get().?;
+ const interp = target.dynamic_linker.get().?;
@memcpy(buffer[0..interp.len], interp);
buffer[interp.len] = 0;
const contents = buffer[0 .. interp.len + 1];
@@ -5065,7 +5083,7 @@ fn writeSyntheticSections(self: *Elf) !void {
}
fn writeSyntheticSectionsObject(self: *Elf) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
for (self.output_rela_sections.values()) |sec| {
if (sec.atom_list.items.len == 0) continue;
@@ -5135,7 +5153,7 @@ fn writeSyntheticSectionsObject(self: *Elf) !void {
}
fn writeComdatGroups(self: *Elf) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
for (self.comdat_group_sections.items) |cgs| {
const shdr = self.shdrs.items[cgs.shndx];
const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
@@ -5160,7 +5178,8 @@ fn writeShStrtab(self: *Elf) !void {
}
fn writeSymtab(self: *Elf) !void {
- const gpa = self.base.allocator;
+ const target = self.base.comp.root_mod.resolved_target.result;
+ const gpa = self.base.comp.gpa;
const symtab_shdr = self.shdrs.items[self.symtab_section_index.?];
const strtab_shdr = self.shdrs.items[self.strtab_section_index.?];
const sym_size: u64 = switch (self.ptr_width) {
@@ -5220,7 +5239,7 @@ fn writeSymtab(self: *Elf) !void {
self.plt_got.writeSymtab(self);
}
- const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian();
+ const foreign_endian = target.cpu.arch.endian() != builtin.cpu.arch.endian();
switch (self.ptr_width) {
.p32 => {
const buf = try gpa.alloc(elf.Elf32_Sym, self.symtab.items.len);
@@ -5299,7 +5318,8 @@ fn ptrWidthBytes(self: Elf) u8 {
/// Does not necessarily match `ptrWidthBytes` for example can be 2 bytes
/// in a 32-bit ELF file.
pub fn archPtrWidthBytes(self: Elf) u8 {
- return @as(u8, @intCast(@divExact(self.base.options.target.ptrBitWidth(), 8)));
+ const target = self.base.comp.root_mod.resolved_target.result;
+ return @intCast(@divExact(target.ptrBitWidth(), 8));
}
fn phdrTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {
@@ -5694,7 +5714,7 @@ pub const AddSectionOpts = struct {
};
pub fn addSection(self: *Elf, opts: AddSectionOpts) !u16 {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const index = @as(u16, @intCast(self.shdrs.items.len));
const shdr = try self.shdrs.addOne(gpa);
shdr.* = .{
@@ -5887,7 +5907,7 @@ const GetOrPutGlobalResult = struct {
};
pub fn getOrPutGlobal(self: *Elf, name: []const u8) !GetOrPutGlobalResult {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const name_off = try self.strings.insert(gpa, name);
const gop = try self.resolver.getOrPut(gpa, name_off);
if (!gop.found_existing) {
@@ -5923,7 +5943,7 @@ const GetOrCreateComdatGroupOwnerResult = struct {
};
pub fn getOrCreateComdatGroupOwner(self: *Elf, name: [:0]const u8) !GetOrCreateComdatGroupOwnerResult {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const off = try self.strings.insert(gpa, name);
const gop = try self.comdat_groups_table.getOrPut(gpa, off);
if (!gop.found_existing) {
@@ -6039,7 +6059,7 @@ pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 {
}
fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const max_notes = 4;
try self.misc_errors.ensureUnusedCapacity(gpa, undefs.count());
src/link/MachO.zig
@@ -143,14 +143,23 @@ tlv_table: TlvSymbolTable = .{},
/// Hot-code swapping state.
hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},
-pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
- assert(options.target.ofmt == .macho);
+darwin_sdk_layout: ?SdkLayout,
+
+/// The filesystem layout of darwin SDK elements.
+pub const SdkLayout = enum {
+ /// macOS SDK layout: TOP { /usr/include, /usr/lib, /System/Library/Frameworks }.
+ sdk,
+ /// Shipped libc layout: TOP { /lib/libc/include, /lib/libc/darwin, <NONE> }.
+ vendored,
+};
- if (options.emit == null) {
- return createEmpty(allocator, options);
- }
+pub fn open(arena: Allocator, options: link.File.OpenOptions) !*MachO {
+ if (build_options.only_c) unreachable;
+ const target = options.comp.root_mod.resolved_target.result;
+ assert(target.ofmt == .macho);
- const emit = options.emit.?;
+ const gpa = options.comp.gpa;
+ const emit = options.emit;
const mode: Mode = mode: {
if (options.use_llvm or options.module == null or options.cache_mode == .whole)
break :mode .zld;
@@ -160,17 +169,16 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
if (options.module == null) {
// No point in opening a file, we would not write anything to it.
// Initialize with empty.
- return createEmpty(allocator, options);
+ return createEmpty(arena, options);
}
// Open a temporary object file, not the final output file because we
// want to link with LLD.
- break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{
- emit.sub_path, options.target.ofmt.fileExt(options.target.cpu.arch),
+ break :blk try std.fmt.allocPrint(arena, "{s}{s}", .{
+ emit.sub_path, target.ofmt.fileExt(target.cpu.arch),
});
} else emit.sub_path;
- errdefer if (mode == .zld) allocator.free(sub_path);
- const self = try createEmpty(allocator, options);
+ const self = try createEmpty(arena, options);
errdefer self.base.destroy();
if (mode == .zld) {
@@ -186,7 +194,6 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
.read = true,
.mode = link.determineMode(options),
});
- errdefer file.close();
self.base.file = file;
if (!options.strip and options.module != null) {
@@ -194,11 +201,10 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
log.debug("creating {s}.dSYM bundle", .{sub_path});
const d_sym_path = try std.fmt.allocPrint(
- allocator,
+ arena,
"{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF",
.{sub_path},
);
- defer allocator.free(d_sym_path);
var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{});
defer d_sym_bundle.close();
@@ -209,21 +215,21 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
});
self.d_sym = .{
- .allocator = allocator,
- .dwarf = link.File.Dwarf.init(allocator, &self.base, .dwarf32),
+ .allocator = gpa,
+ .dwarf = link.File.Dwarf.init(gpa, &self.base, .dwarf32),
.file = d_sym_file,
};
}
// Index 0 is always a null symbol.
- try self.locals.append(allocator, .{
+ try self.locals.append(gpa, .{
.n_strx = 0,
.n_type = 0,
.n_sect = 0,
.n_desc = 0,
.n_value = 0,
});
- try self.strtab.buffer.append(allocator, 0);
+ try self.strtab.buffer.append(gpa, 0);
try self.populateMissingMetadata();
@@ -234,15 +240,14 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
return self;
}
-pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
- const self = try gpa.create(MachO);
- errdefer gpa.destroy(self);
+pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*MachO {
+ const self = try arena.create(MachO);
self.* = .{
.base = .{
.tag = .macho,
- .options = options,
- .allocator = gpa,
+ .comp = options.comp,
+ .emit = options.emit,
.file = null,
},
.mode = if (options.use_llvm or options.module == null or options.cache_mode == .whole)
@@ -252,7 +257,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
};
if (options.use_llvm and options.module != null) {
- self.llvm_object = try LlvmObject.create(gpa, options);
+ self.llvm_object = try LlvmObject.create(arena, options);
}
log.debug("selected linker mode '{s}'", .{@tagName(self.mode)});
@@ -261,20 +266,15 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
}
pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {
- if (self.base.options.emit == null) {
- if (self.llvm_object) |llvm_object| {
- try llvm_object.flushModule(comp, prog_node);
- }
- return;
- }
+ const gpa = self.base.comp.gpa;
if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Static) {
if (build_options.have_llvm) {
return self.base.linkAsArchive(comp, prog_node);
} else {
- try self.misc_errors.ensureUnusedCapacity(self.base.allocator, 1);
+ try self.misc_errors.ensureUnusedCapacity(gpa, 1);
self.misc_errors.appendAssumeCapacity(.{
- .msg = try self.base.allocator.dupe(u8, "TODO: non-LLVM archiver for MachO object files"),
+ .msg = try gpa.dupe(u8, "TODO: non-LLVM archiver for MachO object files"),
});
return error.FlushFailure;
}
@@ -294,7 +294,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
return try llvm_object.flushModule(comp, prog_node);
}
- var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
+ const gpa = self.base.comp.gpa;
+ var arena_allocator = std.heap.ArenaAllocator.init(gpa);
defer arena_allocator.deinit();
const arena = arena_allocator.allocator();
@@ -391,7 +392,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
if (cache_miss) {
for (self.dylibs.items) |*dylib| {
- dylib.deinit(self.base.allocator);
+ dylib.deinit(gpa);
}
self.dylibs.clearRetainingCapacity();
self.dylibs_map.clearRetainingCapacity();
@@ -403,7 +404,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
const in_file = try std.fs.cwd().openFile(path, .{});
defer in_file.close();
- var parse_ctx = ParseErrorCtx.init(self.base.allocator);
+ var parse_ctx = ParseErrorCtx.init(gpa);
defer parse_ctx.deinit();
self.parseLibrary(
@@ -470,7 +471,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
const section = self.sections.get(sym.n_sect - 1).header;
const file_offset = section.offset + sym.n_value - section.addr;
- var code = std.ArrayList(u8).init(self.base.allocator);
+ var code = std.ArrayList(u8).init(gpa);
defer code.deinit();
try code.resize(math.cast(usize, atom.size) orelse return error.Overflow);
@@ -518,12 +519,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
var codesig = CodeSignature.init(getPageSize(self.base.options.target.cpu.arch));
codesig.code_directory.ident = self.base.options.emit.?.sub_path;
if (self.base.options.entitlements) |path| {
- try codesig.addEntitlements(self.base.allocator, path);
+ try codesig.addEntitlements(gpa, path);
}
try self.writeCodeSignaturePadding(&codesig);
break :blk codesig;
} else null;
- defer if (codesig) |*csig| csig.deinit(self.base.allocator);
+ defer if (codesig) |*csig| csig.deinit(gpa);
// Write load commands
var lc_buffer = std.ArrayList(u8).init(arena);
@@ -555,12 +556,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
});
},
.Lib => if (self.base.options.link_mode == .Dynamic) {
- try load_commands.writeDylibIdLC(self.base.allocator, &self.base.options, lc_writer);
+ try load_commands.writeDylibIdLC(gpa, &self.base.options, lc_writer);
},
else => {},
}
- try load_commands.writeRpathLCs(self.base.allocator, &self.base.options, lc_writer);
+ try load_commands.writeRpathLCs(gpa, &self.base.options, lc_writer);
try lc_writer.writeStruct(macho.source_version_command{
.version = 0,
});
@@ -644,7 +645,8 @@ pub fn resolveLibSystem(
search_dirs: []const []const u8,
out_libs: anytype,
) !void {
- var tmp_arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
+ const gpa = self.base.comp.gpa;
+ var tmp_arena_allocator = std.heap.ArenaAllocator.init(gpa);
defer tmp_arena_allocator.deinit();
const tmp_arena = tmp_arena_allocator.allocator();
@@ -775,7 +777,7 @@ fn parseObject(
const tracy = trace(@src());
defer tracy.end();
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const mtime: u64 = mtime: {
const stat = file.stat() catch break :mtime 0;
break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
@@ -868,7 +870,7 @@ pub fn parseFatLibrary(
cpu_arch: std.Target.Cpu.Arch,
ctx: *ParseErrorCtx,
) ParseError!u64 {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const fat_archs = try fat.parseArchs(gpa, file);
defer gpa.free(fat_archs);
@@ -892,7 +894,7 @@ fn parseArchive(
must_link: bool,
ctx: *ParseErrorCtx,
) ParseError!void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
// We take ownership of the file so that we can store it for the duration of symbol resolution.
// TODO we shouldn't need to do that and could pre-parse the archive like we do for zld/ELF?
@@ -973,7 +975,7 @@ fn parseDylib(
dylib_options: DylibOpts,
ctx: *ParseErrorCtx,
) ParseError!void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const file_stat = try file.stat();
const file_size = math.cast(usize, file_stat.size - offset) orelse return error.Overflow;
@@ -1019,7 +1021,7 @@ fn parseLibStub(
dylib_options: DylibOpts,
ctx: *ParseErrorCtx,
) ParseError!void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var lib_stub = try LibStub.loadFromFile(gpa, file);
defer lib_stub.deinit();
@@ -1072,7 +1074,7 @@ fn addDylib(self: *MachO, dylib: Dylib, dylib_options: DylibOpts, ctx: *ParseErr
}
}
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const gop = try self.dylibs_map.getOrPut(gpa, dylib.id.?.name);
if (gop.found_existing) return error.DylibAlreadyExists;
@@ -1098,7 +1100,7 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void {
// 2) afterwards, we parse dependents of the included dylibs
// TODO this should not be performed if the user specifies `-flat_namespace` flag.
// See ld64 manpages.
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
while (dependent_libs.readItem()) |dep_id| {
defer dep_id.id.deinit(gpa);
@@ -1162,7 +1164,8 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void {
log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
// Gather relocs which can be resolved.
- var relocs = std.ArrayList(*Relocation).init(self.base.allocator);
+ const gpa = self.base.comp.gpa;
+ var relocs = std.ArrayList(*Relocation).init(gpa);
defer relocs.deinit();
if (self.relocs.getPtr(atom_index)) |rels| {
@@ -1237,7 +1240,7 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
fn writeStubHelperPreamble(self: *MachO) !void {
if (self.stub_helper_preamble_allocated) return;
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const cpu_arch = self.base.options.target.cpu.arch;
const size = stubs.stubHelperPreambleSize(cpu_arch);
@@ -1290,7 +1293,7 @@ fn writeStubTableEntry(self: *MachO, index: usize) !void {
self.stub_table_count_dirty = false;
}
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const stubs_header = self.sections.items(.header)[stubs_sect_id];
const stub_helper_header = self.sections.items(.header)[stub_helper_sect_id];
@@ -1469,7 +1472,7 @@ const CreateAtomOpts = struct {
};
pub fn createAtom(self: *MachO, sym_index: u32, opts: CreateAtomOpts) !Atom.Index {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const index = @as(Atom.Index, @intCast(self.atoms.items.len));
const atom = try self.atoms.addOne(gpa);
atom.* = .{};
@@ -1481,7 +1484,7 @@ pub fn createAtom(self: *MachO, sym_index: u32, opts: CreateAtomOpts) !Atom.Inde
}
pub fn createTentativeDefAtoms(self: *MachO) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
for (self.globals.items) |global| {
const sym = self.getSymbolPtr(global);
@@ -1536,7 +1539,8 @@ pub fn createDyldPrivateAtom(self: *MachO) !void {
.size = @sizeOf(u64),
.alignment = .@"8",
});
- try self.atom_by_index_table.putNoClobber(self.base.allocator, sym_index, atom_index);
+ const gpa = self.base.comp.gpa;
+ try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);
if (self.data_section_index == null) {
self.data_section_index = try self.initSection("__DATA", "__data", .{});
@@ -1560,7 +1564,7 @@ pub fn createDyldPrivateAtom(self: *MachO) !void {
}
fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: SymbolWithLoc) !Atom.Index {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const size = 3 * @sizeOf(u64);
const required_alignment: Alignment = .@"1";
const sym_index = try self.allocateSymbol();
@@ -1595,7 +1599,7 @@ fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: S
pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {
if (self.base.options.output_mode != .Exe) return;
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const sym_index = try self.allocateSymbol();
const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
const sym = self.getSymbolPtr(sym_loc);
@@ -1622,7 +1626,7 @@ pub fn createDsoHandleSymbol(self: *MachO) !void {
const global = self.getGlobalPtr("___dso_handle") orelse return;
if (!self.getSymbol(global.*).undf()) return;
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const sym_index = try self.allocateSymbol();
const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
const sym = self.getSymbolPtr(sym_loc);
@@ -1686,7 +1690,7 @@ pub fn resolveSymbols(self: *MachO) !void {
}
fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const sym = self.getSymbol(current);
const sym_name = self.getSymbolName(current);
@@ -1800,7 +1804,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u32) !void {
fn resolveSymbolsInArchives(self: *MachO) !void {
if (self.archives.items.len == 0) return;
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var next_sym: usize = 0;
loop: while (next_sym < self.unresolved.count()) {
const global = self.globals.items[self.unresolved.keys()[next_sym]];
@@ -1829,7 +1833,7 @@ fn resolveSymbolsInArchives(self: *MachO) !void {
fn resolveSymbolsInDylibs(self: *MachO) !void {
if (self.dylibs.items.len == 0) return;
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var next_sym: usize = 0;
loop: while (next_sym < self.unresolved.count()) {
const global_index = self.unresolved.keys()[next_sym];
@@ -1899,6 +1903,7 @@ fn resolveSymbolsAtLoading(self: *MachO) !void {
}
fn resolveBoundarySymbols(self: *MachO) !void {
+ const gpa = self.base.comp.gpa;
var next_sym: usize = 0;
while (next_sym < self.unresolved.count()) {
const global_index = self.unresolved.keys()[next_sym];
@@ -1909,7 +1914,7 @@ fn resolveBoundarySymbols(self: *MachO) !void {
const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
const sym = self.getSymbolPtr(sym_loc);
sym.* = .{
- .n_strx = try self.strtab.insert(self.base.allocator, self.getSymbolName(global.*)),
+ .n_strx = try self.strtab.insert(gpa, self.getSymbolName(global.*)),
.n_type = macho.N_SECT | macho.N_EXT,
.n_sect = 0,
.n_desc = N_BOUNDARY,
@@ -1929,9 +1934,9 @@ fn resolveBoundarySymbols(self: *MachO) !void {
}
pub fn deinit(self: *MachO) void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
- if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);
+ if (self.llvm_object) |llvm_object| llvm_object.deinit();
if (self.d_sym) |*d_sym| {
d_sym.deinit();
@@ -2032,7 +2037,7 @@ pub fn deinit(self: *MachO) void {
}
fn freeAtom(self: *MachO, atom_index: Atom.Index) void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
log.debug("freeAtom {d}", .{atom_index});
// Remove any relocs and base relocs associated with this Atom
@@ -2124,7 +2129,8 @@ fn growAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment:
}
pub fn allocateSymbol(self: *MachO) !u32 {
- try self.locals.ensureUnusedCapacity(self.base.allocator, 1);
+ const gpa = self.base.comp.gpa;
+ try self.locals.ensureUnusedCapacity(gpa, 1);
const index = blk: {
if (self.locals_free_list.popOrNull()) |index| {
@@ -2150,7 +2156,8 @@ pub fn allocateSymbol(self: *MachO) !u32 {
}
fn allocateGlobal(self: *MachO) !u32 {
- try self.globals.ensureUnusedCapacity(self.base.allocator, 1);
+ const gpa = self.base.comp.gpa;
+ try self.globals.ensureUnusedCapacity(gpa, 1);
const index = blk: {
if (self.globals_free_list.popOrNull()) |index| {
@@ -2171,7 +2178,8 @@ fn allocateGlobal(self: *MachO) !u32 {
pub fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {
if (self.got_table.lookup.contains(target)) return;
- const got_index = try self.got_table.allocateEntry(self.base.allocator, target);
+ const gpa = self.base.comp.gpa;
+ const got_index = try self.got_table.allocateEntry(gpa, target);
if (self.got_section_index == null) {
self.got_section_index = try self.initSection("__DATA_CONST", "__got", .{
.flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
@@ -2186,7 +2194,8 @@ pub fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {
pub fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {
if (self.stub_table.lookup.contains(target)) return;
- const stub_index = try self.stub_table.allocateEntry(self.base.allocator, target);
+ const gpa = self.base.comp.gpa;
+ const stub_index = try self.stub_table.allocateEntry(gpa, target);
if (self.stubs_section_index == null) {
self.stubs_section_index = try self.initSection("__TEXT", "__stubs", .{
.flags = macho.S_SYMBOL_STUBS |
@@ -2212,7 +2221,8 @@ pub fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {
pub fn addTlvPtrEntry(self: *MachO, target: SymbolWithLoc) !void {
if (self.tlv_ptr_table.lookup.contains(target)) return;
- _ = try self.tlv_ptr_table.allocateEntry(self.base.allocator, target);
+ const gpa = self.base.comp.gpa;
+ _ = try self.tlv_ptr_table.allocateEntry(gpa, target);
if (self.tlv_ptr_section_index == null) {
self.tlv_ptr_section_index = try self.initSection("__DATA", "__thread_ptrs", .{
.flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
@@ -2236,7 +2246,8 @@ pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air:
self.freeUnnamedConsts(decl_index);
Atom.freeRelocations(self, atom_index);
- var code_buffer = std.ArrayList(u8).init(self.base.allocator);
+ const gpa = self.base.comp.gpa;
+ var code_buffer = std.ArrayList(u8).init(gpa);
defer code_buffer.deinit();
var decl_state = if (self.d_sym) |*d_sym|
@@ -2279,7 +2290,7 @@ pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air:
}
pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: InternPool.DeclIndex) !u32 {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const mod = self.base.options.module.?;
const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);
if (!gop.found_existing) {
@@ -2318,7 +2329,7 @@ fn lowerConst(
sect_id: u8,
src_loc: Module.SrcLoc,
) !LowerConstResult {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var code_buffer = std.ArrayList(u8).init(gpa);
defer code_buffer.deinit();
@@ -2366,6 +2377,7 @@ pub fn updateDecl(self: *MachO, mod: *Module, decl_index: InternPool.DeclIndex)
const tracy = trace(@src());
defer tracy.end();
+ const gpa = self.base.comp.gpa;
const decl = mod.declPtr(decl_index);
if (decl.val.getExternFunc(mod)) |_| {
@@ -2375,8 +2387,8 @@ pub fn updateDecl(self: *MachO, mod: *Module, decl_index: InternPool.DeclIndex)
if (decl.isExtern(mod)) {
// TODO make this part of getGlobalSymbol
const name = mod.intern_pool.stringToSlice(decl.name);
- const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name});
- defer self.base.allocator.free(sym_name);
+ const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});
+ defer gpa.free(sym_name);
_ = try self.addUndefined(sym_name, .{ .add_got = true });
return;
}
@@ -2391,7 +2403,7 @@ pub fn updateDecl(self: *MachO, mod: *Module, decl_index: InternPool.DeclIndex)
const sym_index = self.getAtom(atom_index).getSymbolIndex().?;
Atom.freeRelocations(self, atom_index);
- var code_buffer = std.ArrayList(u8).init(self.base.allocator);
+ var code_buffer = std.ArrayList(u8).init(gpa);
defer code_buffer.deinit();
var decl_state: ?Dwarf.DeclState = if (self.d_sym) |*d_sym|
@@ -2449,7 +2461,7 @@ fn updateLazySymbolAtom(
atom_index: Atom.Index,
section_index: u8,
) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const mod = self.base.options.module.?;
var required_alignment: Alignment = .none;
@@ -2515,7 +2527,8 @@ fn updateLazySymbolAtom(
pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.Index {
const mod = self.base.options.module.?;
- const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(mod));
+ const gpa = self.base.comp.gpa;
+ const gop = try self.lazy_syms.getOrPut(gpa, sym.getDecl(mod));
errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
if (!gop.found_existing) gop.value_ptr.* = .{};
const metadata: struct { atom: *Atom.Index, state: *LazySymbolMetadata.State } = switch (sym.kind) {
@@ -2529,7 +2542,7 @@ pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.In
.unused => {
const sym_index = try self.allocateSymbol();
metadata.atom.* = try self.createAtom(sym_index, .{});
- try self.atom_by_index_table.putNoClobber(self.base.allocator, sym_index, metadata.atom.*);
+ try self.atom_by_index_table.putNoClobber(gpa, sym_index, metadata.atom.*);
},
.pending_flush => return metadata.atom.*,
.flushed => {},
@@ -2556,7 +2569,7 @@ fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: InternPo
const init_sym_index = init_atom.getSymbolIndex().?;
Atom.freeRelocations(self, init_atom_index);
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var code_buffer = std.ArrayList(u8).init(gpa);
defer code_buffer.deinit();
@@ -2640,11 +2653,12 @@ fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: InternPo
}
pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: InternPool.DeclIndex) !Atom.Index {
- const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
+ const gpa = self.base.comp.gpa;
+ const gop = try self.decls.getOrPut(gpa, decl_index);
if (!gop.found_existing) {
const sym_index = try self.allocateSymbol();
const atom_index = try self.createAtom(sym_index, .{});
- try self.atom_by_index_table.putNoClobber(self.base.allocator, sym_index, atom_index);
+ try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);
gop.value_ptr.* = .{
.atom = atom_index,
.section = self.getDeclOutputSection(decl_index),
@@ -2694,7 +2708,7 @@ fn getDeclOutputSection(self: *MachO, decl_index: InternPool.DeclIndex) u8 {
}
fn updateDeclCode(self: *MachO, decl_index: InternPool.DeclIndex, code: []u8) !u64 {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const mod = self.base.options.module.?;
const decl = mod.declPtr(decl_index);
@@ -2787,7 +2801,7 @@ pub fn updateExports(
const tracy = trace(@src());
defer tracy.end();
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const metadata = switch (exported) {
.decl_index => |decl_index| blk: {
@@ -2912,7 +2926,7 @@ pub fn deleteDeclExport(
if (self.llvm_object) |_| return;
const metadata = self.decls.getPtr(decl_index) orelse return;
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const mod = self.base.options.module.?;
const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{mod.intern_pool.stringToSlice(name)});
defer gpa.free(exp_name);
@@ -2941,7 +2955,7 @@ pub fn deleteDeclExport(
}
fn freeUnnamedConsts(self: *MachO, decl_index: InternPool.DeclIndex) void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
for (unnamed_consts.items) |atom| {
self.freeAtom(atom);
@@ -2951,6 +2965,7 @@ fn freeUnnamedConsts(self: *MachO, decl_index: InternPool.DeclIndex) void {
pub fn freeDecl(self: *MachO, decl_index: InternPool.DeclIndex) void {
if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
+ const gpa = self.base.comp.gpa;
const mod = self.base.options.module.?;
const decl = mod.declPtr(decl_index);
@@ -2960,7 +2975,7 @@ pub fn freeDecl(self: *MachO, decl_index: InternPool.DeclIndex) void {
var kv = const_kv;
self.freeAtom(kv.value.atom);
self.freeUnnamedConsts(decl_index);
- kv.value.exports.deinit(self.base.allocator);
+ kv.value.exports.deinit(gpa);
}
if (self.d_sym) |*d_sym| {
@@ -2993,7 +3008,7 @@ pub fn lowerAnonDecl(
explicit_alignment: InternPool.Alignment,
src_loc: Module.SrcLoc,
) !codegen.Result {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const mod = self.base.options.module.?;
const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val));
const decl_alignment = switch (explicit_alignment) {
@@ -3060,7 +3075,7 @@ pub fn getAnonDeclVAddr(self: *MachO, decl_val: InternPool.Index, reloc_info: li
fn populateMissingMetadata(self: *MachO) !void {
assert(self.mode == .incremental);
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const cpu_arch = self.base.options.target.cpu.arch;
const pagezero_vmsize = self.calcPagezeroSize();
@@ -3228,7 +3243,8 @@ const InitSectionOpts = struct {
pub fn initSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: InitSectionOpts) !u8 {
log.debug("creating section '{s},{s}'", .{ segname, sectname });
const index = @as(u8, @intCast(self.sections.slice().len));
- try self.sections.append(self.base.allocator, .{
+ const gpa = self.base.comp.gpa;
+ try self.sections.append(gpa, .{
.segment_index = undefined, // Segments will be created automatically later down the pipeline
.header = .{
.sectname = makeStaticString(sectname),
@@ -3248,7 +3264,7 @@ fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts
flags: u32 = macho.S_REGULAR,
reserved2: u32 = 0,
}) !u8 {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const page_size = getPageSize(self.base.options.target.cpu.arch);
// In incremental context, we create one section per segment pairing. This way,
// we can move the segment in raw file as we please.
@@ -3521,7 +3537,7 @@ fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignm
pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 {
_ = lib_name;
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});
defer gpa.free(sym_name);
return self.addUndefined(sym_name, .{ .add_stub = true });
@@ -3582,7 +3598,7 @@ pub fn writeLinkeditSegmentData(self: *MachO) !void {
}
fn collectRebaseDataFromTableSection(self: *MachO, sect_id: u8, rebase: *Rebase, table: anytype) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const header = self.sections.items(.header)[sect_id];
const segment_index = self.sections.items(.segment_index)[sect_id];
const segment = self.segments.items[segment_index];
@@ -3605,7 +3621,7 @@ fn collectRebaseDataFromTableSection(self: *MachO, sect_id: u8, rebase: *Rebase,
}
fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const slice = self.sections.slice();
for (self.rebases.keys(), 0..) |atom_index, i| {
@@ -3715,7 +3731,7 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
}
fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, table: anytype) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const header = self.sections.items(.header)[sect_id];
const segment_index = self.sections.items(.segment_index)[sect_id];
const segment = self.segments.items[segment_index];
@@ -3746,7 +3762,7 @@ fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, tab
}
fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const slice = self.sections.slice();
for (raw_bindings.keys(), 0..) |atom_index, i| {
@@ -3885,12 +3901,13 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
fn collectLazyBindData(self: *MachO, bind: anytype) !void {
const sect_id = self.la_symbol_ptr_section_index orelse return;
+ const gpa = self.base.comp.gpa;
try self.collectBindDataFromTableSection(sect_id, bind, self.stub_table);
- try bind.finalize(self.base.allocator, self);
+ try bind.finalize(gpa, self);
}
fn collectExportData(self: *MachO, trie: *Trie) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
// TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
log.debug("generating export trie", .{});
@@ -3922,7 +3939,7 @@ fn writeDyldInfoData(self: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var rebase = Rebase{};
defer rebase.deinit(gpa);
@@ -4046,7 +4063,7 @@ fn addSymbolToFunctionStarts(self: *MachO, sym_loc: SymbolWithLoc, addresses: *s
}
fn writeFunctionStarts(self: *MachO) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const seg = self.segments.items[self.header_segment_cmd_index.?];
// We need to sort by address first
@@ -4133,7 +4150,7 @@ fn filterDataInCode(
}
pub fn writeDataInCode(self: *MachO) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var out_dice = std.ArrayList(macho.data_in_code_entry).init(gpa);
defer out_dice.deinit();
@@ -4211,13 +4228,14 @@ fn addLocalToSymtab(self: *MachO, sym_loc: SymbolWithLoc, locals: *std.ArrayList
if (sym.n_desc == N_BOUNDARY) return; // boundary symbol, skip
if (sym.ext()) return; // an export lands in its own symtab section, skip
if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip
+ const gpa = self.base.comp.gpa;
var out_sym = sym;
- out_sym.n_strx = try self.strtab.insert(self.base.allocator, self.getSymbolName(sym_loc));
+ out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(sym_loc));
try locals.append(out_sym);
}
fn writeSymtab(self: *MachO) !SymtabCtx {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var locals = std.ArrayList(macho.nlist_64).init(gpa);
defer locals.deinit();
@@ -4322,7 +4340,7 @@ fn generateSymbolStabs(
) !void {
log.debug("generating stabs for '{s}'", .{object.name});
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
var debug_info = object.parseDwarfInfo();
var lookup = DwarfInfo.AbbrevLookupTable.init(gpa);
@@ -4450,7 +4468,7 @@ fn generateSymbolStabsForSymbol(
lookup: ?DwarfInfo.SubprogramLookupByName,
buf: *[4]macho.nlist_64,
) ![]const macho.nlist_64 {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const object = self.objects.items[sym_loc.getFile().?];
const sym = self.getSymbol(sym_loc);
const sym_name = self.getSymbolName(sym_loc);
@@ -4536,7 +4554,7 @@ fn generateSymbolStabsForSymbol(
}
pub fn writeStrtab(self: *MachO) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const seg = self.getLinkeditSegmentPtr();
const offset = seg.fileoff + seg.filesize;
assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
@@ -4565,7 +4583,7 @@ const SymtabCtx = struct {
};
pub fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const nstubs = @as(u32, @intCast(self.stub_table.lookup.count()));
const ngot_entries = @as(u32, @intCast(self.got_table.lookup.count()));
const nindirectsyms = nstubs * 2 + ngot_entries;
@@ -4671,7 +4689,8 @@ pub fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *Cod
const seg = self.segments.items[seg_id];
const offset = self.codesig_cmd.dataoff;
- var buffer = std.ArrayList(u8).init(self.base.allocator);
+ const gpa = self.base.comp.gpa;
+ var buffer = std.ArrayList(u8).init(gpa);
defer buffer.deinit();
try buffer.ensureTotalCapacityPrecise(code_sig.size());
try code_sig.writeAdhocSignature(comp, .{
@@ -4817,7 +4836,7 @@ pub fn ptraceDetach(self: *MachO, pid: std.os.pid_t) !void {
}
pub fn addUndefined(self: *MachO, name: []const u8, flags: RelocFlags) !u32 {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const gop = try self.getOrPutGlobalPtr(name);
const global_index = self.getGlobalIndex(name).?;
@@ -4842,7 +4861,8 @@ pub fn addUndefined(self: *MachO, name: []const u8, flags: RelocFlags) !u32 {
}
fn updateRelocActions(self: *MachO, global_index: u32, flags: RelocFlags) !void {
- const act_gop = try self.actions.getOrPut(self.base.allocator, global_index);
+ const gpa = self.base.comp.gpa;
+ const act_gop = try self.actions.getOrPut(gpa, global_index);
if (!act_gop.found_existing) {
act_gop.value_ptr.* = .{};
}
@@ -5022,7 +5042,7 @@ pub fn getOrPutGlobalPtr(self: *MachO, name: []const u8) !GetOrPutGlobalPtrResul
if (self.getGlobalPtr(name)) |ptr| {
return GetOrPutGlobalPtrResult{ .found_existing = true, .value_ptr = ptr };
}
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const global_index = try self.allocateGlobal();
const global_name = try gpa.dupe(u8, name);
_ = try self.resolver.put(gpa, global_name, global_index);
@@ -5171,6 +5191,7 @@ pub fn handleAndReportParseError(
err: ParseError,
ctx: *const ParseErrorCtx,
) error{OutOfMemory}!void {
+ const gpa = self.base.comp.gpa;
const cpu_arch = self.base.options.target.cpu.arch;
switch (err) {
error.DylibAlreadyExists => {},
@@ -5188,7 +5209,7 @@ pub fn handleAndReportParseError(
},
error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}),
error.InvalidTarget, error.InvalidTargetFatLibrary => {
- var targets_string = std.ArrayList(u8).init(self.base.allocator);
+ var targets_string = std.ArrayList(u8).init(gpa);
defer targets_string.deinit();
if (ctx.detected_targets.items.len > 1) {
@@ -5226,7 +5247,7 @@ fn reportMissingLibraryError(
comptime format: []const u8,
args: anytype,
) error{OutOfMemory}!void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
try self.misc_errors.ensureUnusedCapacity(gpa, 1);
const notes = try gpa.alloc(File.ErrorMsg, checked_paths.len);
errdefer gpa.free(notes);
@@ -5246,7 +5267,7 @@ fn reportDependencyError(
comptime format: []const u8,
args: anytype,
) error{OutOfMemory}!void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
try self.misc_errors.ensureUnusedCapacity(gpa, 1);
var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 2);
defer notes.deinit();
@@ -5266,7 +5287,7 @@ pub fn reportParseError(
comptime format: []const u8,
args: anytype,
) error{OutOfMemory}!void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
try self.misc_errors.ensureUnusedCapacity(gpa, 1);
var notes = try gpa.alloc(File.ErrorMsg, 1);
errdefer gpa.free(notes);
@@ -5283,7 +5304,7 @@ pub fn reportUnresolvedBoundarySymbol(
comptime format: []const u8,
args: anytype,
) error{OutOfMemory}!void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
try self.misc_errors.ensureUnusedCapacity(gpa, 1);
var notes = try gpa.alloc(File.ErrorMsg, 1);
errdefer gpa.free(notes);
@@ -5295,7 +5316,7 @@ pub fn reportUnresolvedBoundarySymbol(
}
pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
const count = self.unresolved.count();
try self.misc_errors.ensureUnusedCapacity(gpa, count);
@@ -5327,7 +5348,7 @@ fn reportSymbolCollision(
first: SymbolWithLoc,
other: SymbolWithLoc,
) error{OutOfMemory}!void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
try self.misc_errors.ensureUnusedCapacity(gpa, 1);
var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 2);
@@ -5355,7 +5376,7 @@ fn reportSymbolCollision(
}
fn reportUnhandledSymbolType(self: *MachO, sym_with_loc: SymbolWithLoc) error{OutOfMemory}!void {
- const gpa = self.base.allocator;
+ const gpa = self.base.comp.gpa;
try self.misc_errors.ensureUnusedCapacity(gpa, 1);
const notes = try gpa.alloc(File.ErrorMsg, 1);
src/Package/Module.zig
@@ -1,6 +1,6 @@
//! Corresponds to something that Zig source code can `@import`.
-//! Not to be confused with src/Module.zig which should be renamed
-//! to something else. https://github.com/ziglang/zig/issues/14307
+//! Not to be confused with src/Module.zig which will be renamed
+//! to Zcu. https://github.com/ziglang/zig/issues/14307
/// Only files inside this directory can be imported.
root: Package.Path,
@@ -14,6 +14,26 @@ fully_qualified_name: []const u8,
/// responsible for detecting these names and using the correct package.
deps: Deps = .{},
+resolved_target: ResolvedTarget,
+optimize_mode: std.builtin.OptimizeMode,
+code_model: std.builtin.CodeModel,
+single_threaded: bool,
+error_tracing: bool,
+valgrind: bool,
+pic: bool,
+strip: bool,
+omit_frame_pointer: bool,
+stack_check: bool,
+stack_protector: u32,
+red_zone: bool,
+sanitize_c: bool,
+sanitize_thread: bool,
+unwind_tables: bool,
+cc_argv: []const []const u8,
+
+/// The contents of `@import("builtin")` for this module.
+generated_builtin_source: []const u8,
+
pub const Deps = std.StringArrayHashMapUnmanaged(*Module);
pub const Tree = struct {
@@ -21,10 +41,382 @@ pub const Tree = struct {
build_module_table: std.AutoArrayHashMapUnmanaged(MultiHashHexDigest, *Module),
};
-pub fn create(allocator: Allocator, m: Module) Allocator.Error!*Module {
- const new = try allocator.create(Module);
- new.* = m;
- return new;
+pub const CreateOptions = struct {
+ /// Where to store builtin.zig. The global cache directory is used because
+ /// it is a pure function based on CLI flags.
+ global_cache_directory: Cache.Directory,
+ paths: Paths,
+ fully_qualified_name: []const u8,
+
+ cc_argv: []const []const u8,
+ inherited: Inherited,
+ global: Compilation.Config,
+ /// If this is null then `resolved_target` must be non-null.
+ parent: ?*Package.Module,
+
+ builtin_mod: ?*Package.Module,
+
+ pub const Paths = struct {
+ root: Package.Path,
+ /// Relative to `root`. May contain path separators.
+ root_src_path: []const u8,
+ };
+
+ pub const Inherited = struct {
+ /// If this is null then `parent` must be non-null.
+ resolved_target: ?ResolvedTarget = null,
+ optimize_mode: ?std.builtin.OptimizeMode = null,
+ code_model: ?std.builtin.CodeModel = null,
+ single_threaded: ?bool = null,
+ error_tracing: ?bool = null,
+ valgrind: ?bool = null,
+ pic: ?bool = null,
+ strip: ?bool = null,
+ omit_frame_pointer: ?bool = null,
+ stack_check: ?bool = null,
+ /// null means default.
+ /// 0 means no stack protector.
+ /// other number means stack protection with that buffer size.
+ stack_protector: ?u32 = null,
+ red_zone: ?bool = null,
+ unwind_tables: ?bool = null,
+ sanitize_c: ?bool = null,
+ sanitize_thread: ?bool = null,
+ };
+};
+
+pub const ResolvedTarget = struct {
+ result: std.Target,
+ is_native_os: bool,
+ is_native_abi: bool,
+ llvm_cpu_features: ?[*:0]const u8 = null,
+};
+
+/// At least one of `parent` and `resolved_target` must be non-null.
+pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
+ const resolved_target = options.inherited.resolved_target orelse options.parent.?.resolved_target;
+ const target = resolved_target.result;
+
+ const optimize_mode = options.inherited.optimize_mode orelse
+ if (options.parent) |p| p.optimize_mode else .Debug;
+
+ const unwind_tables = options.inherited.unwind_tables orelse
+ if (options.parent) |p| p.unwind_tables else options.global.any_unwind_tables;
+
+ const strip = b: {
+ if (options.inherited.strip) |x| break :b x;
+ if (options.parent) |p| break :b p.strip;
+ if (optimize_mode == .ReleaseSmall) break :b true;
+ if (!target_util.hasDebugInfo(target)) break :b true;
+ break :b false;
+ };
+
+ const valgrind = b: {
+ if (!target_util.hasValgrindSupport(target)) {
+ if (options.inherited.valgrind == true)
+ return error.ValgrindUnsupportedOnTarget;
+ break :b false;
+ }
+ if (options.inherited.valgrind) |x| break :b x;
+ if (options.parent) |p| break :b p.valgrind;
+ if (strip) break :b false;
+ break :b optimize_mode == .Debug;
+ };
+
+ const zig_backend = target_util.zigBackend(target, options.global.use_llvm);
+
+ const single_threaded = b: {
+ if (target_util.alwaysSingleThreaded(target)) {
+ if (options.inherited.single_threaded == false)
+ return error.TargetRequiresSingleThreaded;
+ break :b true;
+ }
+
+ if (options.global.have_zcu) {
+ if (!target_util.supportsThreads(target, zig_backend)) {
+ if (options.inherited.single_threaded == false)
+ return error.BackendRequiresSingleThreaded;
+ break :b true;
+ }
+ }
+
+ if (options.inherited.single_threaded) |x| break :b x;
+ if (options.parent) |p| break :b p.single_threaded;
+ break :b target_util.defaultSingleThreaded(target);
+ };
+
+ const error_tracing = b: {
+ if (options.inherited.error_tracing) |x| break :b x;
+ if (options.parent) |p| break :b p.error_tracing;
+ if (strip) break :b false;
+ break :b switch (optimize_mode) {
+ .Debug => true,
+ .ReleaseSafe, .ReleaseFast, .ReleaseSmall => false,
+ };
+ };
+
+ const pic = b: {
+ if (target_util.requiresPIC(target, options.global.link_libc)) {
+ if (options.inherited.pic == false)
+ return error.TargetRequiresPic;
+ break :b true;
+ }
+ if (options.global.pie) {
+ if (options.inherited.pic == false)
+ return error.PieRequiresPic;
+ break :b true;
+ }
+ if (options.global.link_mode == .Dynamic) {
+ if (options.inherited.pic == false)
+ return error.DynamicLinkingRequiresPic;
+ break :b true;
+ }
+ if (options.inherited.pic) |x| break :b x;
+ if (options.parent) |p| break :b p.pic;
+ break :b false;
+ };
+
+ const red_zone = b: {
+ if (!target_util.hasRedZone(target)) {
+ if (options.inherited.red_zone == true)
+ return error.TargetHasNoRedZone;
+ break :b true;
+ }
+ if (options.inherited.red_zone) |x| break :b x;
+ if (options.parent) |p| break :b p.red_zone;
+ break :b true;
+ };
+
+ const omit_frame_pointer = b: {
+ if (options.inherited.omit_frame_pointer) |x| break :b x;
+ if (options.parent) |p| break :b p.omit_frame_pointer;
+ if (optimize_mode == .Debug) break :b false;
+ break :b true;
+ };
+
+ const sanitize_thread = b: {
+ if (options.inherited.sanitize_thread) |x| break :b x;
+ if (options.parent) |p| break :b p.sanitize_thread;
+ break :b false;
+ };
+
+ const code_model = b: {
+ if (options.inherited.code_model) |x| break :b x;
+ if (options.parent) |p| break :b p.code_model;
+ break :b .default;
+ };
+
+ const is_safe_mode = switch (optimize_mode) {
+ .Debug, .ReleaseSafe => true,
+ .ReleaseFast, .ReleaseSmall => false,
+ };
+
+ const sanitize_c = b: {
+ if (options.inherited.sanitize_c) |x| break :b x;
+ if (options.parent) |p| break :b p.sanitize_c;
+ break :b is_safe_mode;
+ };
+
+ const stack_check = b: {
+ if (!target_util.supportsStackProbing(target)) {
+ if (options.inherited.stack_check == true)
+ return error.StackCheckUnsupportedByTarget;
+ break :b false;
+ }
+ if (options.inherited.stack_check) |x| break :b x;
+ if (options.parent) |p| break :b p.stack_check;
+ break :b is_safe_mode;
+ };
+
+ const stack_protector: u32 = sp: {
+ if (!target_util.supportsStackProtector(target, zig_backend)) {
+ if (options.inherited.stack_protector) |x| {
+ if (x > 0) return error.StackProtectorUnsupportedByTarget;
+ }
+ break :sp 0;
+ }
+
+ // This logic is checking for linking libc because otherwise our start code
+ // which is trying to set up TLS (i.e. the fs/gs registers) but the stack
+ // protection code depends on fs/gs registers being already set up.
+ // If we were able to annotate start code, or perhaps the entire std lib,
+ // as being exempt from stack protection checks, we could change this logic
+ // to supporting stack protection even when not linking libc.
+ // TODO file issue about this
+ if (!options.global.link_libc) {
+ if (options.inherited.stack_protector) |x| {
+ if (x > 0) return error.StackProtectorUnavailableWithoutLibC;
+ }
+ break :sp 0;
+ }
+
+ if (options.inherited.stack_protector) |x| break :sp x;
+ if (options.parent) |p| break :sp p.stack_protector;
+ if (!is_safe_mode) break :sp 0;
+
+ break :sp target_util.default_stack_protector_buffer_size;
+ };
+
+ const llvm_cpu_features: ?[*:0]const u8 = b: {
+ if (resolved_target.llvm_cpu_features) |x| break :b x;
+ if (!options.global.use_llvm) break :b null;
+
+ var buf = std.ArrayList(u8).init(arena);
+ for (target.cpu.arch.allFeaturesList(), 0..) |feature, index_usize| {
+ const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize));
+ const is_enabled = target.cpu.features.isEnabled(index);
+
+ if (feature.llvm_name) |llvm_name| {
+ const plus_or_minus = "-+"[@intFromBool(is_enabled)];
+ try buf.ensureUnusedCapacity(2 + llvm_name.len);
+ buf.appendAssumeCapacity(plus_or_minus);
+ buf.appendSliceAssumeCapacity(llvm_name);
+ buf.appendSliceAssumeCapacity(",");
+ }
+ }
+ if (buf.items.len == 0) break :b "";
+ assert(std.mem.endsWith(u8, buf.items, ","));
+ buf.items[buf.items.len - 1] = 0;
+ buf.shrinkAndFree(buf.items.len);
+ break :b buf.items[0 .. buf.items.len - 1 :0].ptr;
+ };
+
+ const builtin_mod = options.builtin_mod orelse b: {
+ const generated_builtin_source = try Builtin.generate(.{
+ .target = target,
+ .zig_backend = zig_backend,
+ .output_mode = options.global.output_mode,
+ .link_mode = options.global.link_mode,
+ .is_test = options.global.is_test,
+ .test_evented_io = options.global.test_evented_io,
+ .single_threaded = single_threaded,
+ .link_libc = options.global.link_libc,
+ .link_libcpp = options.global.link_libcpp,
+ .optimize_mode = optimize_mode,
+ .error_tracing = error_tracing,
+ .valgrind = valgrind,
+ .sanitize_thread = sanitize_thread,
+ .pic = pic,
+ .pie = options.global.pie,
+ .strip = strip,
+ .code_model = code_model,
+ .omit_frame_pointer = omit_frame_pointer,
+ .wasi_exec_model = options.global.wasi_exec_model,
+ }, arena);
+
+ const digest = Cache.HashHelper.oneShot(generated_builtin_source);
+ const builtin_sub_path = try arena.dupe(u8, "b" ++ std.fs.path.sep_str ++ digest);
+ const new = try arena.create(Module);
+ new.* = .{
+ .root = .{
+ .root_dir = options.global_cache_directory,
+ .sub_path = builtin_sub_path,
+ },
+ .root_src_path = "builtin.zig",
+ .fully_qualified_name = if (options.parent == null)
+ "builtin"
+ else
+ try std.fmt.allocPrint(arena, "{s}.builtin", .{options.fully_qualified_name}),
+ .resolved_target = .{
+ .result = target,
+ .is_native_os = resolved_target.is_native_os,
+ .is_native_abi = resolved_target.is_native_abi,
+ .llvm_cpu_features = llvm_cpu_features,
+ },
+ .optimize_mode = optimize_mode,
+ .single_threaded = single_threaded,
+ .error_tracing = error_tracing,
+ .valgrind = valgrind,
+ .pic = pic,
+ .strip = strip,
+ .omit_frame_pointer = omit_frame_pointer,
+ .stack_check = stack_check,
+ .stack_protector = stack_protector,
+ .code_model = code_model,
+ .red_zone = red_zone,
+ .generated_builtin_source = generated_builtin_source,
+ .sanitize_c = sanitize_c,
+ .sanitize_thread = sanitize_thread,
+ .unwind_tables = unwind_tables,
+ .cc_argv = &.{},
+ };
+ break :b new;
+ };
+
+ const mod = try arena.create(Module);
+ mod.* = .{
+ .root = options.paths.root,
+ .root_src_path = options.paths.root_src_path,
+ .fully_qualified_name = options.fully_qualified_name,
+ .resolved_target = .{
+ .result = target,
+ .is_native_os = resolved_target.is_native_os,
+ .is_native_abi = resolved_target.is_native_abi,
+ .llvm_cpu_features = llvm_cpu_features,
+ },
+ .optimize_mode = optimize_mode,
+ .single_threaded = single_threaded,
+ .error_tracing = error_tracing,
+ .valgrind = valgrind,
+ .pic = pic,
+ .strip = strip,
+ .omit_frame_pointer = omit_frame_pointer,
+ .stack_check = stack_check,
+ .stack_protector = stack_protector,
+ .code_model = code_model,
+ .red_zone = red_zone,
+ .generated_builtin_source = builtin_mod.generated_builtin_source,
+ .sanitize_c = sanitize_c,
+ .sanitize_thread = sanitize_thread,
+ .unwind_tables = unwind_tables,
+ .cc_argv = options.cc_argv,
+ };
+
+ try mod.deps.ensureUnusedCapacity(arena, 1);
+ mod.deps.putAssumeCapacityNoClobber("builtin", builtin_mod);
+
+ return mod;
+}
+
+/// All fields correspond to `CreateOptions`.
+pub const LimitedOptions = struct {
+ root: Package.Path,
+ root_src_path: []const u8,
+ fully_qualified_name: []const u8,
+};
+
+/// This one can only be used if the Module will only be used for AstGen and earlier in
+/// the pipeline. Illegal behavior occurs if a limited module touches Sema.
+pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*Package.Module {
+ const mod = try gpa.create(Module);
+ mod.* = .{
+ .root = options.root,
+ .root_src_path = options.root_src_path,
+ .fully_qualified_name = options.fully_qualified_name,
+
+ .resolved_target = undefined,
+ .optimize_mode = undefined,
+ .code_model = undefined,
+ .single_threaded = undefined,
+ .error_tracing = undefined,
+ .valgrind = undefined,
+ .pic = undefined,
+ .strip = undefined,
+ .omit_frame_pointer = undefined,
+ .stack_check = undefined,
+ .stack_protector = undefined,
+ .red_zone = undefined,
+ .sanitize_c = undefined,
+ .sanitize_thread = undefined,
+ .unwind_tables = undefined,
+ .cc_argv = undefined,
+ .generated_builtin_source = undefined,
+ };
+ return mod;
+}
+
+pub fn getBuiltinDependency(m: *Module) *Module {
+ return m.deps.values()[0];
}
const Module = @This();
@@ -32,3 +424,8 @@ const Package = @import("../Package.zig");
const std = @import("std");
const Allocator = std.mem.Allocator;
const MultiHashHexDigest = Package.Manifest.MultiHashHexDigest;
+const target_util = @import("../target.zig");
+const Cache = std.Build.Cache;
+const Builtin = @import("../Builtin.zig");
+const assert = std.debug.assert;
+const Compilation = @import("../Compilation.zig");
src/Builtin.zig
@@ -0,0 +1,240 @@
+target: std.Target,
+zig_backend: std.builtin.CompilerBackend,
+output_mode: std.builtin.OutputMode,
+link_mode: std.builtin.LinkMode,
+is_test: bool,
+test_evented_io: bool,
+single_threaded: bool,
+link_libc: bool,
+link_libcpp: bool,
+optimize_mode: std.builtin.OptimizeMode,
+error_tracing: bool,
+valgrind: bool,
+sanitize_thread: bool,
+pic: bool,
+pie: bool,
+strip: bool,
+code_model: std.builtin.CodeModel,
+omit_frame_pointer: bool,
+wasi_exec_model: std.builtin.WasiExecModel,
+
+pub fn generate(opts: @This(), allocator: Allocator) Allocator.Error![:0]u8 {
+ var buffer = std.ArrayList(u8).init(allocator);
+ defer buffer.deinit();
+
+ const target = opts.target;
+ const generic_arch_name = target.cpu.arch.genericName();
+ const zig_backend = opts.zig_backend;
+
+ @setEvalBranchQuota(4000);
+ try buffer.writer().print(
+ \\const std = @import("std");
+ \\/// Zig version. When writing code that supports multiple versions of Zig, prefer
+ \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.
+ \\pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable;
+ \\pub const zig_version_string = "{s}";
+ \\pub const zig_backend = std.builtin.CompilerBackend.{};
+ \\
+ \\pub const output_mode = std.builtin.OutputMode.{};
+ \\pub const link_mode = std.builtin.LinkMode.{};
+ \\pub const is_test = {};
+ \\pub const single_threaded = {};
+ \\pub const abi = std.Target.Abi.{};
+ \\pub const cpu: std.Target.Cpu = .{{
+ \\ .arch = .{},
+ \\ .model = &std.Target.{}.cpu.{},
+ \\ .features = std.Target.{}.featureSet(&[_]std.Target.{}.Feature{{
+ \\
+ , .{
+ build_options.version,
+ std.zig.fmtId(@tagName(zig_backend)),
+ std.zig.fmtId(@tagName(opts.output_mode)),
+ std.zig.fmtId(@tagName(opts.link_mode)),
+ opts.is_test,
+ opts.single_threaded,
+ std.zig.fmtId(@tagName(target.abi)),
+ std.zig.fmtId(@tagName(target.cpu.arch)),
+ std.zig.fmtId(generic_arch_name),
+ std.zig.fmtId(target.cpu.model.name),
+ std.zig.fmtId(generic_arch_name),
+ std.zig.fmtId(generic_arch_name),
+ });
+
+ for (target.cpu.arch.allFeaturesList(), 0..) |feature, index_usize| {
+ const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize));
+ const is_enabled = target.cpu.features.isEnabled(index);
+ if (is_enabled) {
+ try buffer.writer().print(" .{},\n", .{std.zig.fmtId(feature.name)});
+ }
+ }
+ try buffer.writer().print(
+ \\ }}),
+ \\}};
+ \\pub const os = std.Target.Os{{
+ \\ .tag = .{},
+ \\ .version_range = .{{
+ ,
+ .{std.zig.fmtId(@tagName(target.os.tag))},
+ );
+
+ switch (target.os.getVersionRange()) {
+ .none => try buffer.appendSlice(" .none = {} },\n"),
+ .semver => |semver| try buffer.writer().print(
+ \\ .semver = .{{
+ \\ .min = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ .max = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ }}}},
+ \\
+ , .{
+ semver.min.major,
+ semver.min.minor,
+ semver.min.patch,
+
+ semver.max.major,
+ semver.max.minor,
+ semver.max.patch,
+ }),
+ .linux => |linux| try buffer.writer().print(
+ \\ .linux = .{{
+ \\ .range = .{{
+ \\ .min = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ .max = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ }},
+ \\ .glibc = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ }}}},
+ \\
+ , .{
+ linux.range.min.major,
+ linux.range.min.minor,
+ linux.range.min.patch,
+
+ linux.range.max.major,
+ linux.range.max.minor,
+ linux.range.max.patch,
+
+ linux.glibc.major,
+ linux.glibc.minor,
+ linux.glibc.patch,
+ }),
+ .windows => |windows| try buffer.writer().print(
+ \\ .windows = .{{
+ \\ .min = {s},
+ \\ .max = {s},
+ \\ }}}},
+ \\
+ ,
+ .{ windows.min, windows.max },
+ ),
+ }
+ try buffer.appendSlice(
+ \\};
+ \\pub const target: std.Target = .{
+ \\ .cpu = cpu,
+ \\ .os = os,
+ \\ .abi = abi,
+ \\ .ofmt = object_format,
+ \\
+ );
+
+ if (target.dynamic_linker.get()) |dl| {
+ try buffer.writer().print(
+ \\ .dynamic_linker = std.Target.DynamicLinker.init("{s}"),
+ \\}};
+ \\
+ , .{dl});
+ } else {
+ try buffer.appendSlice(
+ \\ .dynamic_linker = std.Target.DynamicLinker.none,
+ \\};
+ \\
+ );
+ }
+
+ // This is so that compiler_rt and libc.zig libraries know whether they
+ // will eventually be linked with libc. They make different decisions
+ // about what to export depending on whether another libc will be linked
+ // in. For example, compiler_rt will not export the __chkstk symbol if it
+ // knows libc will provide it, and likewise c.zig will not export memcpy.
+ const link_libc = opts.link_libc;
+
+ try buffer.writer().print(
+ \\pub const object_format = std.Target.ObjectFormat.{};
+ \\pub const mode = std.builtin.OptimizeMode.{};
+ \\pub const link_libc = {};
+ \\pub const link_libcpp = {};
+ \\pub const have_error_return_tracing = {};
+ \\pub const valgrind_support = {};
+ \\pub const sanitize_thread = {};
+ \\pub const position_independent_code = {};
+ \\pub const position_independent_executable = {};
+ \\pub const strip_debug_info = {};
+ \\pub const code_model = std.builtin.CodeModel.{};
+ \\pub const omit_frame_pointer = {};
+ \\
+ , .{
+ std.zig.fmtId(@tagName(target.ofmt)),
+ std.zig.fmtId(@tagName(opts.optimize_mode)),
+ link_libc,
+ opts.link_libcpp,
+ opts.error_tracing,
+ opts.valgrind,
+ opts.sanitize_thread,
+ opts.pic,
+ opts.pie,
+ opts.strip,
+ std.zig.fmtId(@tagName(opts.code_model)),
+ opts.omit_frame_pointer,
+ });
+
+ if (target.os.tag == .wasi) {
+ const wasi_exec_model_fmt = std.zig.fmtId(@tagName(opts.wasi_exec_model));
+ try buffer.writer().print(
+ \\pub const wasi_exec_model = std.builtin.WasiExecModel.{};
+ \\
+ , .{wasi_exec_model_fmt});
+ }
+
+ if (opts.is_test) {
+ try buffer.appendSlice(
+ \\pub var test_functions: []const std.builtin.TestFn = undefined; // overwritten later
+ \\
+ );
+ if (opts.test_evented_io) {
+ try buffer.appendSlice(
+ \\pub const test_io_mode = .evented;
+ \\
+ );
+ } else {
+ try buffer.appendSlice(
+ \\pub const test_io_mode = .blocking;
+ \\
+ );
+ }
+ }
+
+ return buffer.toOwnedSliceSentinel(0);
+}
+
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const build_options = @import("build_options");
src/Compilation.zig
@@ -38,12 +38,43 @@ const Autodoc = @import("Autodoc.zig");
const Color = @import("main.zig").Color;
const resinator = @import("resinator.zig");
+pub const Config = @import("Compilation/Config.zig");
+
/// General-purpose allocator. Used for both temporary and long-term storage.
gpa: Allocator,
/// Arena-allocated memory, mostly used during initialization. However, it can be used
/// for other things requiring the same lifetime as the `Compilation`.
arena: std.heap.ArenaAllocator,
-bin_file: *link.File,
+/// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
+/// TODO: rename to zcu: ?*Zcu
+module: ?*Module,
+/// All compilations have a root module because this is where some important
+/// settings are stored, such as target and optimization mode. This module
+/// might not have any .zig code associated with it, however.
+root_mod: *Package.Module,
+
+/// User-specified settings that have all the defaults resolved into concrete values.
+config: Config,
+
+/// This is `null` when `-fno-emit-bin` is used.
+bin_file: ?*link.File,
+
+/// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin)
+sysroot: ?[]const u8,
+/// This is `null` when not building a Windows DLL, or when `-fno-emit-implib` is used.
+implib_emit: ?Emit,
+/// This is non-null when `-femit-docs` is provided.
+docs_emit: ?Emit,
+root_name: [:0]const u8,
+cache_mode: CacheMode,
+include_compiler_rt: bool,
+objects: []Compilation.LinkObject,
+/// These are *always* dynamically linked. Static libraries will be
+/// provided as positional arguments.
+system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
+version: ?std.SemanticVersion,
+libc_installation: ?*const LibCInstallation,
+
c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{},
win32_resource_table: if (build_options.only_core_functionality) void else std.AutoArrayHashMapUnmanaged(*Win32Resource, void) =
if (build_options.only_core_functionality) {} else .{},
@@ -87,8 +118,6 @@ failed_win32_resources: if (build_options.only_core_functionality) void else std
misc_failures: std.AutoArrayHashMapUnmanaged(MiscTask, MiscError) = .{},
keep_source_files_loaded: bool,
-c_frontend: CFrontend,
-sanitize_c: bool,
/// When this is `true` it means invoking clang as a sub-process is expected to inherit
/// stdin, stdout, stderr, and if it returns non success, to forward the exit code.
/// Otherwise we attempt to parse the error messages and expose them via the Compilation API.
@@ -107,8 +136,6 @@ verbose_llvm_cpu_features: bool,
disable_c_depfile: bool,
time_report: bool,
stack_report: bool,
-unwind_tables: bool,
-test_evented_io: bool,
debug_compiler_runtime_libs: bool,
debug_compile_errors: bool,
job_queued_compiler_rt_lib: bool = false,
@@ -118,7 +145,6 @@ formatted_panics: bool = false,
last_update_was_cache_hit: bool = false,
c_source_files: []const CSourceFile,
-clang_argv: []const []const u8,
rc_source_files: []const RcSourceFile,
cache_parent: *Cache,
/// Path to own executable for invoking `zig clang`.
@@ -194,7 +220,29 @@ emit_llvm_bc: ?EmitLoc,
work_queue_wait_group: WaitGroup = .{},
astgen_wait_group: WaitGroup = .{},
-pub const default_stack_protector_buffer_size = 4;
+pub const Emit = struct {
+ /// Where the output will go.
+ directory: Directory,
+ /// Path to the output file, relative to `directory`.
+ sub_path: []const u8,
+
+ /// Returns the full path to `basename` if it were in the same directory as the
+ /// `Emit` sub_path.
+ pub fn basenamePath(emit: Emit, arena: Allocator, basename: [:0]const u8) ![:0]const u8 {
+ const full_path = if (emit.directory.path) |p|
+ try std.fs.path.join(arena, &[_][]const u8{ p, emit.sub_path })
+ else
+ emit.sub_path;
+
+ if (std.fs.path.dirname(full_path)) |dirname| {
+ return try std.fs.path.joinZ(arena, &.{ dirname, basename });
+ } else {
+ return basename;
+ }
+ }
+};
+
+pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size;
pub const SemaError = Module.SemaError;
pub const CRTFile = struct {
@@ -208,8 +256,8 @@ pub const CRTFile = struct {
}
};
-// supported languages for "zig clang -x <lang>".
-// Loosely based on llvm-project/clang/include/clang/Driver/Types.def
+/// Supported languages for "zig clang -x <lang>".
+/// Loosely based on llvm-project/clang/include/clang/Driver/Types.def
pub const LangToExt = std.ComptimeStringMap(FileExt, .{
.{ "c", .c },
.{ "c-header", .h },
@@ -226,16 +274,20 @@ pub const LangToExt = std.ComptimeStringMap(FileExt, .{
/// For passing to a C compiler.
pub const CSourceFile = struct {
+ /// Many C compiler flags are determined by settings contained in the owning Module.
+ owner: *Package.Module,
src_path: []const u8,
extra_flags: []const []const u8 = &.{},
/// Same as extra_flags except they are not added to the Cache hash.
cache_exempt_flags: []const []const u8 = &.{},
- // this field is non-null iff language was explicitly set with "-x lang".
+ /// This field is non-null if and only if the language was explicitly set
+ /// with "-x lang".
ext: ?FileExt = null,
};
/// For passing to resinator.
pub const RcSourceFile = struct {
+ owner: *Package.Module,
src_path: []const u8,
extra_flags: []const []const u8 = &.{},
};
@@ -742,6 +794,22 @@ pub const EmitLoc = struct {
};
pub const cache_helpers = struct {
+ pub fn addResolvedTarget(
+ hh: *Cache.HashHelper,
+ resolved_target: Package.Module.ResolvedTarget,
+ ) void {
+ const target = resolved_target.result;
+ hh.add(target.cpu.arch);
+ hh.addBytes(target.cpu.model.name);
+ hh.add(target.cpu.features.ints);
+ hh.add(target.os.tag);
+ hh.add(target.os.getVersionRange());
+ hh.add(target.abi);
+ hh.add(target.ofmt);
+ hh.add(resolved_target.is_native_os);
+ hh.add(resolved_target.is_native_abi);
+ }
+
pub fn addEmitLoc(hh: *Cache.HashHelper, emit_loc: EmitLoc) void {
hh.addBytes(emit_loc.basename);
}
@@ -751,7 +819,7 @@ pub const cache_helpers = struct {
addEmitLoc(hh, optional_emit_loc orelse return);
}
- pub fn hashCSource(self: *Cache.Manifest, c_source: Compilation.CSourceFile) !void {
+ pub fn hashCSource(self: *Cache.Manifest, c_source: CSourceFile) !void {
_ = try self.addFile(c_source.src_path, null);
// Hash the extra flags, with special care to call addFile for file parameters.
// TODO this logic can likely be improved by utilizing clang_options_data.zig.
@@ -770,8 +838,6 @@ pub const cache_helpers = struct {
}
};
-pub const CFrontend = enum { clang, aro };
-
pub const ClangPreprocessorMode = enum {
no,
/// This means we are doing `zig cc -E -o <path>`.
@@ -798,11 +864,21 @@ pub const InitOptions = struct {
zig_lib_directory: Directory,
local_cache_directory: Directory,
global_cache_directory: Directory,
- target: Target,
- root_name: []const u8,
- main_mod: ?*Package.Module,
- output_mode: std.builtin.OutputMode,
thread_pool: *ThreadPool,
+ self_exe_path: ?[]const u8 = null,
+
+ /// Options that have been resolved by calling `resolveDefaults`.
+ config: Compilation.Config,
+
+ root_mod: *Package.Module,
+ /// Normally, `main_mod` and `root_mod` are the same. The exception is `zig
+ /// test`, in which `root_mod` is the test runner, and `main_mod` is the
+ /// user's source file which has the tests.
+ main_mod: ?*Package.Module,
+ /// This is provided so that the API user has a chance to tweak the
+ /// per-module settings of the standard library.
+ std_mod: *Package.Module,
+ root_name: []const u8,
sysroot: ?[]const u8 = null,
/// `null` means to not emit a binary file.
emit_bin: ?EmitLoc,
@@ -818,7 +894,6 @@ pub const InitOptions = struct {
emit_docs: ?EmitLoc = null,
/// `null` means to not emit an import lib.
emit_implib: ?EmitLoc = null,
- link_mode: ?std.builtin.LinkMode = null,
dll_export_fns: ?bool = false,
/// Normally when using LLD to link, Zig uses a file named "lld.id" in the
/// same directory as the output binary which contains the hash of the link
@@ -828,14 +903,12 @@ pub const InitOptions = struct {
/// this flag would be set to disable this machinery to avoid false positives.
disable_lld_caching: bool = false,
cache_mode: CacheMode = .incremental,
- optimize_mode: std.builtin.OptimizeMode = .Debug,
keep_source_files_loaded: bool = false,
- clang_argv: []const []const u8 = &[0][]const u8{},
lib_dirs: []const []const u8 = &[0][]const u8{},
rpath_list: []const []const u8 = &[0][]const u8{},
symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{},
- c_source_files: []const CSourceFile = &[0]CSourceFile{},
- rc_source_files: []const RcSourceFile = &[0]RcSourceFile{},
+ c_source_files: []const CSourceFile = &.{},
+ rc_source_files: []const RcSourceFile = &.{},
manifest_file: ?[]const u8 = null,
rc_includes: RcIncludes = .any,
link_objects: []LinkObject = &[0]LinkObject{},
@@ -849,40 +922,16 @@ pub const InitOptions = struct {
/// * mman
/// * signal
wasi_emulated_libs: []const wasi_libc.CRTFile = &[0]wasi_libc.CRTFile{},
- link_libc: bool = false,
- link_libcpp: bool = false,
- link_libunwind: bool = false,
- want_pic: ?bool = null,
/// This means that if the output mode is an executable it will be a
/// Position Independent Executable. If the output mode is not an
/// executable this field is ignored.
- want_pie: ?bool = null,
- want_sanitize_c: ?bool = null,
- want_stack_check: ?bool = null,
- /// null means default.
- /// 0 means no stack protector.
- /// other number means stack protection with that buffer size.
- want_stack_protector: ?u32 = null,
- want_red_zone: ?bool = null,
- omit_frame_pointer: ?bool = null,
- want_valgrind: ?bool = null,
- want_tsan: ?bool = null,
want_compiler_rt: ?bool = null,
want_lto: ?bool = null,
- want_unwind_tables: ?bool = null,
- use_llvm: ?bool = null,
- use_lib_llvm: ?bool = null,
- use_lld: ?bool = null,
- use_clang: ?bool = null,
- single_threaded: ?bool = null,
- strip: ?bool = null,
formatted_panics: ?bool = null,
rdynamic: bool = false,
function_sections: bool = false,
data_sections: bool = false,
no_builtin: bool = false,
- is_native_os: bool,
- is_native_abi: bool,
time_report: bool = false,
stack_report: bool = false,
link_eh_frame_hdr: bool = false,
@@ -893,14 +942,11 @@ pub const InitOptions = struct {
linker_gc_sections: ?bool = null,
linker_allow_shlib_undefined: ?bool = null,
linker_bind_global_refs_locally: ?bool = null,
- linker_import_memory: ?bool = null,
- linker_export_memory: ?bool = null,
linker_import_symbols: bool = false,
linker_import_table: bool = false,
linker_export_table: bool = false,
linker_initial_memory: ?u64 = null,
linker_max_memory: ?u64 = null,
- linker_shared_memory: bool = false,
linker_global_base: ?u64 = null,
linker_export_symbol_names: []const []const u8 = &.{},
linker_print_gc_sections: bool = false,
@@ -938,8 +984,6 @@ pub const InitOptions = struct {
verbose_llvm_bc: ?[]const u8 = null,
verbose_cimport: bool = false,
verbose_llvm_cpu_features: bool = false,
- is_test: bool = false,
- test_evented_io: bool = false,
debug_compiler_runtime_libs: bool = false,
debug_compile_errors: bool = false,
/// Normally when you create a `Compilation`, Zig will automatically build
@@ -952,23 +996,18 @@ pub const InitOptions = struct {
force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .{},
stack_size_override: ?u64 = null,
image_base_override: ?u64 = null,
- self_exe_path: ?[]const u8 = null,
version: ?std.SemanticVersion = null,
compatibility_version: ?std.SemanticVersion = null,
libc_installation: ?*const LibCInstallation = null,
- machine_code_model: std.builtin.CodeModel = .default,
clang_preprocessor_mode: ClangPreprocessorMode = .no,
/// This is for stage1 and should be deleted upon completion of self-hosting.
color: Color = .auto,
reference_trace: ?u32 = null,
- error_tracing: ?bool = null,
test_filter: ?[]const u8 = null,
test_name_prefix: ?[]const u8 = null,
test_runner_path: ?[]const u8 = null,
subsystem: ?std.Target.SubSystem = null,
dwarf_format: ?std.dwarf.Format = null,
- /// WASI-only. Type of WASI execution model ("command" or "reactor").
- wasi_exec_model: ?std.builtin.WasiExecModel = null,
/// (Zig compiler development) Enable dumping linker's state as JSON.
enable_link_snapshots: bool = false,
/// (Darwin) Install name of the dylib
@@ -989,77 +1028,93 @@ pub const InitOptions = struct {
pdb_source_path: ?[]const u8 = null,
/// (Windows) PDB output path
pdb_out_path: ?[]const u8 = null,
- error_limit: ?Module.ErrorInt = null,
+ error_limit: ?Compilation.Module.ErrorInt = null,
/// (SPIR-V) whether to generate a structured control flow graph or not
want_structured_cfg: ?bool = null,
};
fn addModuleTableToCacheHash(
+ gpa: Allocator,
+ arena: Allocator,
hash: *Cache.HashHelper,
- arena: *std.heap.ArenaAllocator,
- mod_table: Package.Module.Deps,
- seen_table: *std.AutoHashMap(*Package.Module, void),
+ root_mod: *Package.Module,
hash_type: union(enum) { path_bytes, files: *Cache.Manifest },
) (error{OutOfMemory} || std.os.GetCwdError)!void {
- const allocator = arena.allocator();
-
- const module_indices = try allocator.alloc(u32, mod_table.count());
- // Copy over the hashmap entries to our slice
- for (module_indices, 0..) |*module_index, index| module_index.* = @intCast(index);
- // Sort the slice by package name
- mem.sortUnstable(u32, module_indices, &mod_table, struct {
- fn lessThan(deps: *const Package.Module.Deps, lhs: u32, rhs: u32) bool {
- const keys = deps.keys();
- return std.mem.lessThan(u8, keys[lhs], keys[rhs]);
+ var seen_table: std.AutoArrayHashMapUnmanaged(*Package.Module, void) = .{};
+ try seen_table.put(gpa, root_mod, {});
+
+ const SortByName = struct {
+ names: []const []const u8,
+
+ pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
+ const lhs_key = ctx.names[lhs_index];
+ const rhs_key = ctx.names[rhs_index];
+ return mem.lessThan(u8, lhs_key, rhs_key);
}
- }.lessThan);
+ };
- for (module_indices) |module_index| {
- const module = mod_table.values()[module_index];
- if ((try seen_table.getOrPut(module)).found_existing) continue;
+ var i: usize = 0;
+ while (i < seen_table.count()) : (i += 1) {
+ const mod = seen_table.keys()[i];
+
+ cache_helpers.addResolvedTarget(hash, mod.resolved_target);
+ hash.add(mod.optimize_mode);
+ hash.add(mod.code_model);
+ hash.add(mod.single_threaded);
+ hash.add(mod.error_tracing);
+ hash.add(mod.valgrind);
+ hash.add(mod.pic);
+ hash.add(mod.strip);
+ hash.add(mod.omit_frame_pointer);
+ hash.add(mod.stack_check);
+ hash.add(mod.red_zone);
+ hash.add(mod.sanitize_c);
+ hash.add(mod.sanitize_thread);
- // Finally insert the package name and path to the cache hash.
- hash.addBytes(mod_table.keys()[module_index]);
switch (hash_type) {
.path_bytes => {
- hash.addBytes(module.root_src_path);
- hash.addOptionalBytes(module.root.root_dir.path);
- hash.addBytes(module.root.sub_path);
+ hash.addBytes(mod.root_src_path);
+ hash.addOptionalBytes(mod.root.root_dir.path);
+ hash.addBytes(mod.root.sub_path);
},
.files => |man| {
- const pkg_zig_file = try module.root.joinString(
- allocator,
- module.root_src_path,
- );
+ const pkg_zig_file = try mod.root.joinString(arena, mod.root_src_path);
_ = try man.addFile(pkg_zig_file, null);
},
}
- // Recurse to handle the module's dependencies
- try addModuleTableToCacheHash(hash, arena, module.deps, seen_table, hash_type);
+
+ mod.deps.sortUnstable(SortByName{ .names = mod.deps.keys() });
+
+ hash.addListOfBytes(mod.deps.keys());
+
+ const deps = mod.deps.values();
+ try seen_table.ensureUnusedCapacity(gpa, deps.len);
+ for (deps) |dep| seen_table.putAssumeCapacity(dep, {});
}
}
pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
- const is_dyn_lib = switch (options.output_mode) {
+ const output_mode = options.config.output_mode;
+ const is_dyn_lib = switch (output_mode) {
.Obj, .Exe => false,
- .Lib => (options.link_mode orelse .Static) == .Dynamic,
+ .Lib => options.config.link_mode == .Dynamic,
};
- const is_exe_or_dyn_lib = switch (options.output_mode) {
+ const is_exe_or_dyn_lib = switch (output_mode) {
.Obj => false,
.Lib => is_dyn_lib,
.Exe => true,
};
- // WASI-only. Resolve the optional exec-model option, defaults to command.
- const wasi_exec_model = if (options.target.os.tag != .wasi) undefined else options.wasi_exec_model orelse .command;
-
if (options.linker_export_table and options.linker_import_table) {
return error.ExportTableAndImportTableConflict;
}
+ const have_zcu = options.root_mod.root_src_path.len != 0;
+
const comp: *Compilation = comp: {
- // For allocations that have the same lifetime as Compilation. This arena is used only during this
- // initialization and then is freed in deinit().
+ // For allocations that have the same lifetime as Compilation. This
+ // arena is used only during this initialization and then is freed in
+ // deinit().
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
errdefer arena_allocator.deinit();
const arena = arena_allocator.allocator();
@@ -1069,366 +1124,145 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
const comp = try arena.create(Compilation);
const root_name = try arena.dupeZ(u8, options.root_name);
- // Make a decision on whether to use LLVM or our own backend.
- const use_lib_llvm = options.use_lib_llvm orelse build_options.have_llvm;
- const use_llvm = blk: {
- if (options.use_llvm) |explicit|
- break :blk explicit;
-
- // If emitting to LLVM bitcode object format, must use LLVM backend.
- if (options.emit_llvm_ir != null or options.emit_llvm_bc != null)
- break :blk true;
-
- // If we have no zig code to compile, no need for LLVM.
- if (options.main_mod == null)
- break :blk false;
-
- // If we cannot use LLVM libraries, then our own backends will be a
- // better default since the LLVM backend can only produce bitcode
- // and not an object file or executable.
- if (!use_lib_llvm)
- break :blk false;
-
- // If LLVM does not support the target, then we can't use it.
- if (!target_util.hasLlvmSupport(options.target, options.target.ofmt))
- break :blk false;
-
- // Prefer LLVM for release builds.
- if (options.optimize_mode != .Debug)
- break :blk true;
-
- // At this point we would prefer to use our own self-hosted backend,
- // because the compilation speed is better than LLVM. But only do it if
- // we are confident in the robustness of the backend.
- break :blk !target_util.selfHostedBackendIsAsRobustAsLlvm(options.target);
- };
- if (!use_llvm) {
- if (options.use_llvm == true) {
- return error.ZigCompilerNotBuiltWithLLVMExtensions;
- }
- if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) {
- return error.EmittingLlvmModuleRequiresUsingLlvmBackend;
- }
- }
+ const use_llvm = options.config.use_llvm;
// TODO: once we support incremental compilation for the LLVM backend via
// saving the LLVM module into a bitcode file and restoring it, along with
// compiler state, the second clause here can be removed so that incremental
// cache mode is used for LLVM backend too. We need some fuzz testing before
// that can be enabled.
- const cache_mode = if ((use_llvm or options.main_mod == null) and !options.disable_lld_caching)
+ const cache_mode = if ((use_llvm or !have_zcu) and !options.disable_lld_caching)
CacheMode.whole
else
options.cache_mode;
- const tsan = options.want_tsan orelse false;
- // TSAN is implemented in C++ so it requires linking libc++.
- const link_libcpp = options.link_libcpp or tsan;
- const link_libc = link_libcpp or options.link_libc or options.link_libunwind or
- target_util.osRequiresLibC(options.target);
-
- const link_libunwind = options.link_libunwind or
- (link_libcpp and target_util.libcNeedsLibUnwind(options.target));
- const unwind_tables = options.want_unwind_tables orelse
- (link_libunwind or target_util.needUnwindTables(options.target));
- const link_eh_frame_hdr = options.link_eh_frame_hdr or unwind_tables;
- const build_id = options.build_id orelse .none;
+ const any_unwind_tables = options.config.any_unwind_tables;
- // Make a decision on whether to use LLD or our own linker.
- const use_lld = options.use_lld orelse blk: {
- if (options.target.isDarwin()) {
- break :blk false;
- }
-
- if (!build_options.have_llvm)
- break :blk false;
-
- if (options.target.ofmt == .c)
- break :blk false;
-
- if (options.want_lto) |lto| {
- if (lto) {
- break :blk true;
- }
- }
-
- // Our linker can't handle objects or most advanced options yet.
- if (options.link_objects.len != 0 or
- options.c_source_files.len != 0 or
- options.frameworks.len != 0 or
- options.system_lib_names.len != 0 or
- options.link_libc or options.link_libcpp or
- link_eh_frame_hdr or
- options.link_emit_relocs or
- options.output_mode == .Lib or
- options.linker_script != null or options.version_script != null or
- options.emit_implib != null or
- build_id != .none or
- options.symbol_wrap_set.count() > 0)
- {
- break :blk true;
- }
-
- if (use_llvm) {
- // If stage1 generates an object file, self-hosted linker is not
- // yet sophisticated enough to handle that.
- break :blk options.main_mod != null;
- }
-
- break :blk false;
- };
-
- const lto = blk: {
- if (options.want_lto) |want_lto| {
- if (want_lto and !use_lld and !options.target.isDarwin())
- return error.LtoUnavailableWithoutLld;
- break :blk want_lto;
- } else if (!use_lld) {
- // zig ld LTO support is tracked by
- // https://github.com/ziglang/zig/issues/8680
- break :blk false;
- } else if (options.c_source_files.len == 0) {
- break :blk false;
- } else if (options.target.cpu.arch.isRISCV()) {
- // Clang and LLVM currently don't support RISC-V target-abi for LTO.
- // Compiling with LTO may fail or produce undesired results.
- // See https://reviews.llvm.org/D71387
- // See https://reviews.llvm.org/D102582
- break :blk false;
- } else switch (options.output_mode) {
- .Lib, .Obj => break :blk false,
- .Exe => switch (options.optimize_mode) {
- .Debug => break :blk false,
- .ReleaseSafe, .ReleaseFast, .ReleaseSmall => break :blk true,
- },
- }
- };
-
- const must_dynamic_link = dl: {
- if (target_util.cannotDynamicLink(options.target))
- break :dl false;
- if (is_exe_or_dyn_lib and link_libc and
- (options.target.isGnuLibC() or target_util.osRequiresLibC(options.target)))
- {
- break :dl true;
- }
- const any_dyn_libs: bool = x: {
- if (options.system_lib_names.len != 0)
- break :x true;
- for (options.link_objects) |obj| {
- switch (classifyFileExt(obj.path)) {
- .shared_library => break :x true,
- else => continue,
- }
- }
- break :x false;
- };
- if (any_dyn_libs) {
- // When creating a executable that links to system libraries,
- // we require dynamic linking, but we must not link static libraries
- // or object files dynamically!
- break :dl (options.output_mode == .Exe);
- }
+ const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables;
+ const build_id = options.build_id orelse .none;
- break :dl false;
- };
- const default_link_mode: std.builtin.LinkMode = blk: {
- if (must_dynamic_link) {
- break :blk .Dynamic;
- } else if (is_exe_or_dyn_lib and link_libc and
- options.is_native_abi and options.target.abi.isMusl())
- {
- // If targeting the system's native ABI and the system's
- // libc is musl, link dynamically by default.
- break :blk .Dynamic;
- } else {
- break :blk .Static;
- }
- };
- const link_mode: std.builtin.LinkMode = if (options.link_mode) |lm| blk: {
- if (lm == .Static and must_dynamic_link) {
- return error.UnableToStaticLink;
- }
- break :blk lm;
- } else default_link_mode;
+ const link_libc = options.config.link_libc;
const dll_export_fns = options.dll_export_fns orelse (is_dyn_lib or options.rdynamic);
const libc_dirs = try detectLibCIncludeDirs(
arena,
options.zig_lib_directory.path.?,
- options.target,
- options.is_native_abi,
+ options.root_mod.resolved_target.result,
+ options.root_mod.resolved_target.is_native_abi,
link_libc,
options.libc_installation,
);
- const rc_dirs = try detectWin32ResourceIncludeDirs(
- arena,
- options,
- );
-
- const sysroot = options.sysroot orelse libc_dirs.sysroot;
-
- const pie: bool = pie: {
- if (is_dyn_lib) {
- if (options.want_pie == true) return error.OutputModeForbidsPie;
- break :pie false;
- }
- if (target_util.requiresPIE(options.target)) {
- if (options.want_pie == false) return error.TargetRequiresPie;
- break :pie true;
- }
- if (tsan) {
- if (options.want_pie == false) return error.TsanRequiresPie;
- break :pie true;
- }
- if (options.want_pie) |want_pie| {
- break :pie want_pie;
- }
- break :pie false;
- };
-
- const must_pic: bool = b: {
- if (target_util.requiresPIC(options.target, link_libc))
- break :b true;
- break :b link_mode == .Dynamic;
- };
- const pic = if (options.want_pic) |explicit| pic: {
- if (!explicit) {
- if (must_pic) {
- return error.TargetRequiresPIC;
- }
- if (pie) {
- return error.PIERequiresPIC;
+ // The include directories used when preprocessing .rc files are separate from the
+ // target. Which include directories are used is determined by `options.rc_includes`.
+ //
+ // Note: It should be okay that the include directories used when compiling .rc
+ // files differ from the include directories used when compiling the main
+ // binary, since the .res format is not dependent on anything ABI-related. The
+ // only relevant differences would be things like `#define` constants being
+ // different in the MinGW headers vs the MSVC headers, but any such
+ // differences would likely be a MinGW bug.
+ const rc_dirs = b: {
+ // Set the includes to .none here when there are no rc files to compile
+ var includes = if (options.rc_source_files.len > 0) options.rc_includes else .none;
+ const target = options.root_mod.resolved_target.result;
+ if (!options.root_mod.resolved_target.is_native_os or target.os.tag != .windows) {
+ switch (includes) {
+ // MSVC can't be found when the host isn't Windows, so short-circuit.
+ .msvc => return error.WindowsSdkNotFound,
+ // Skip straight to gnu since we won't be able to detect
+ // MSVC on non-Windows hosts.
+ .any => includes = .gnu,
+ .none, .gnu => {},
}
}
- break :pic explicit;
- } else pie or must_pic;
-
- // Make a decision on whether to use Clang or Aro for translate-c and compiling C files.
- const c_frontend: CFrontend = blk: {
- if (options.use_clang) |want_clang| {
- break :blk if (want_clang) .clang else .aro;
- }
- break :blk if (build_options.have_llvm) .clang else .aro;
- };
- if (!build_options.have_llvm and c_frontend == .clang) {
- return error.ZigCompilerNotBuiltWithLLVMExtensions;
- }
-
- const is_safe_mode = switch (options.optimize_mode) {
- .Debug, .ReleaseSafe => true,
- .ReleaseFast, .ReleaseSmall => false,
- };
-
- const sanitize_c = options.want_sanitize_c orelse is_safe_mode;
-
- const stack_check: bool = options.want_stack_check orelse b: {
- if (!target_util.supportsStackProbing(options.target)) break :b false;
- break :b is_safe_mode;
+ while (true) switch (includes) {
+ .any, .msvc => break :b detectLibCIncludeDirs(
+ arena,
+ options.zig_lib_directory.path.?,
+ .{
+ .cpu = target.cpu,
+ .os = target.os,
+ .abi = .msvc,
+ .ofmt = target.ofmt,
+ },
+ options.root_mod.resolved_target.is_native_abi,
+ // The .rc preprocessor will need to know the libc include dirs even if we
+ // are not linking libc, so force 'link_libc' to true
+ true,
+ options.libc_installation,
+ ) catch |err| {
+ if (includes == .any) {
+ // fall back to mingw
+ includes = .gnu;
+ continue;
+ }
+ return err;
+ },
+ .gnu => break :b try detectLibCFromBuilding(arena, options.zig_lib_directory.path.?, .{
+ .cpu = target.cpu,
+ .os = target.os,
+ .abi = .gnu,
+ .ofmt = target.ofmt,
+ }),
+ .none => break :b LibCDirs{
+ .libc_include_dir_list = &[0][]u8{},
+ .libc_installation = null,
+ .libc_framework_dir_list = &.{},
+ .sysroot = null,
+ .darwin_sdk_layout = null,
+ },
+ };
};
- if (stack_check and !target_util.supportsStackProbing(options.target))
- return error.StackCheckUnsupportedByTarget;
-
- const stack_protector: u32 = sp: {
- const zig_backend = zigBackend(options.target, use_llvm);
- if (!target_util.supportsStackProtector(options.target, zig_backend)) {
- if (options.want_stack_protector) |x| {
- if (x > 0) return error.StackProtectorUnsupportedByTarget;
- }
- break :sp 0;
- }
-
- // This logic is checking for linking libc because otherwise our start code
- // which is trying to set up TLS (i.e. the fs/gs registers) but the stack
- // protection code depends on fs/gs registers being already set up.
- // If we were able to annotate start code, or perhaps the entire std lib,
- // as being exempt from stack protection checks, we could change this logic
- // to supporting stack protection even when not linking libc.
- // TODO file issue about this
- if (!link_libc) {
- if (options.want_stack_protector) |x| {
- if (x > 0) return error.StackProtectorUnavailableWithoutLibC;
- }
- break :sp 0;
- }
- if (options.want_stack_protector) |x| break :sp x;
- if (is_safe_mode) break :sp default_stack_protector_buffer_size;
- break :sp 0;
- };
+ const sysroot = options.sysroot orelse libc_dirs.sysroot;
const include_compiler_rt = options.want_compiler_rt orelse
(!options.skip_linker_dependencies and is_exe_or_dyn_lib);
- const single_threaded = st: {
- if (target_util.isSingleThreaded(options.target)) {
- if (options.single_threaded == false)
- return error.TargetRequiresSingleThreaded;
- break :st true;
- }
- if (options.main_mod != null) {
- const zig_backend = zigBackend(options.target, use_llvm);
- if (!target_util.supportsThreads(options.target, zig_backend)) {
- if (options.single_threaded == false)
- return error.BackendRequiresSingleThreaded;
- break :st true;
- }
- }
- break :st options.single_threaded orelse false;
- };
-
- const llvm_cpu_features: ?[*:0]const u8 = if (use_llvm) blk: {
- var buf = std.ArrayList(u8).init(arena);
- for (options.target.cpu.arch.allFeaturesList(), 0..) |feature, index_usize| {
- const index = @as(Target.Cpu.Feature.Set.Index, @intCast(index_usize));
- const is_enabled = options.target.cpu.features.isEnabled(index);
-
- if (feature.llvm_name) |llvm_name| {
- const plus_or_minus = "-+"[@intFromBool(is_enabled)];
- try buf.ensureUnusedCapacity(2 + llvm_name.len);
- buf.appendAssumeCapacity(plus_or_minus);
- buf.appendSliceAssumeCapacity(llvm_name);
- buf.appendSliceAssumeCapacity(",");
- }
- }
- if (buf.items.len == 0) break :blk "";
- assert(mem.endsWith(u8, buf.items, ","));
- buf.items[buf.items.len - 1] = 0;
- buf.shrinkAndFree(buf.items.len);
- break :blk buf.items[0 .. buf.items.len - 1 :0].ptr;
- } else null;
+ if (include_compiler_rt and output_mode == .Obj) {
+ // For objects, this mechanism relies on essentially `_ = @import("compiler-rt");`
+ // injected into the object.
+ const compiler_rt_mod = try Package.Module.create(arena, .{
+ .global_cache_directory = options.global_cache_directory,
+ .paths = .{
+ .root = .{
+ .root_dir = options.zig_lib_directory,
+ },
+ .root_src_path = "compiler_rt.zig",
+ },
+ .fully_qualified_name = "compiler_rt",
+ .cc_argv = &.{},
+ .inherited = .{},
+ .global = options.config,
+ .parent = options.root_mod,
+ .builtin_mod = options.root_mod.getBuiltinDependency(),
+ });
+ try options.root_mod.deps.putNoClobber(arena, "compiler_rt", compiler_rt_mod);
+ }
if (options.verbose_llvm_cpu_features) {
- if (llvm_cpu_features) |cf| print: {
+ if (options.root_mod.resolved_target.llvm_cpu_features) |cf| print: {
+ const target = options.root_mod.resolved_target.result;
std.debug.getStderrMutex().lock();
defer std.debug.getStderrMutex().unlock();
const stderr = std.io.getStdErr().writer();
- nosuspend stderr.print("compilation: {s}\n", .{options.root_name}) catch break :print;
- nosuspend stderr.print(" target: {s}\n", .{try options.target.zigTriple(arena)}) catch break :print;
- nosuspend stderr.print(" cpu: {s}\n", .{options.target.cpu.model.name}) catch break :print;
- nosuspend stderr.print(" features: {s}\n", .{cf}) catch {};
+ nosuspend {
+ stderr.print("compilation: {s}\n", .{options.root_name}) catch break :print;
+ stderr.print(" target: {s}\n", .{try target.zigTriple(arena)}) catch break :print;
+ stderr.print(" cpu: {s}\n", .{target.cpu.model.name}) catch break :print;
+ stderr.print(" features: {s}\n", .{cf}) catch {};
+ }
}
}
- const strip = options.strip orelse !target_util.hasDebugInfo(options.target);
- const valgrind: bool = b: {
- if (!target_util.hasValgrindSupport(options.target)) break :b false;
- if (options.want_valgrind) |explicit| break :b explicit;
- if (strip) break :b false;
- break :b options.optimize_mode == .Debug;
- };
- if (!valgrind and options.want_valgrind == true)
- return error.ValgrindUnsupportedOnTarget;
-
- const red_zone = options.want_red_zone orelse target_util.hasRedZone(options.target);
- const omit_frame_pointer = options.omit_frame_pointer orelse (options.optimize_mode != .Debug);
- const linker_optimization: u8 = options.linker_optimization orelse switch (options.optimize_mode) {
+ const linker_optimization: u8 = options.linker_optimization orelse switch (options.root_mod.optimize_mode) {
.Debug => @as(u8, 0),
else => @as(u8, 3),
};
- const formatted_panics = options.formatted_panics orelse (options.optimize_mode == .Debug);
+ // TODO: https://github.com/ziglang/zig/issues/17969
+ const formatted_panics = options.formatted_panics orelse (options.root_mod.optimize_mode == .Debug);
const error_limit = options.error_limit orelse (std.math.maxInt(u16) - 1);
@@ -1453,43 +1287,25 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
// This is shared hasher state common to zig source and all C source files.
cache.hash.addBytes(build_options.version);
cache.hash.add(builtin.zig_backend);
- cache.hash.add(options.optimize_mode);
- cache.hash.add(options.target.cpu.arch);
- cache.hash.addBytes(options.target.cpu.model.name);
- cache.hash.add(options.target.cpu.features.ints);
- cache.hash.add(options.target.os.tag);
- cache.hash.add(options.target.os.getVersionRange());
- cache.hash.add(options.is_native_os);
- cache.hash.add(options.target.abi);
- cache.hash.add(options.target.ofmt);
- cache.hash.add(pic);
- cache.hash.add(pie);
- cache.hash.add(lto);
- cache.hash.add(unwind_tables);
- cache.hash.add(tsan);
- cache.hash.add(stack_check);
- cache.hash.add(stack_protector);
- cache.hash.add(red_zone);
- cache.hash.add(omit_frame_pointer);
- cache.hash.add(link_mode);
+ cache.hash.add(options.config.pie);
+ cache.hash.add(options.config.lto);
+ cache.hash.add(options.config.link_mode);
cache.hash.add(options.function_sections);
cache.hash.add(options.data_sections);
cache.hash.add(options.no_builtin);
- cache.hash.add(strip);
cache.hash.add(link_libc);
- cache.hash.add(link_libcpp);
- cache.hash.add(link_libunwind);
- cache.hash.add(options.output_mode);
- cache.hash.add(options.machine_code_model);
+ cache.hash.add(options.config.link_libcpp);
+ cache.hash.add(options.config.link_libunwind);
+ cache.hash.add(output_mode);
cache.hash.addOptional(options.dwarf_format);
cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_bin);
cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_implib);
cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_docs);
cache.hash.addBytes(options.root_name);
- if (options.target.os.tag == .wasi) cache.hash.add(wasi_exec_model);
+ cache.hash.add(options.config.wasi_exec_model);
// TODO audit this and make sure everything is in it
- const module: ?*Module = if (options.main_mod) |main_mod| blk: {
+ const zcu: ?*Module = if (have_zcu) blk: {
// Options that are specific to zig source files, that cannot be
// modified between incremental updates.
var hash = cache.hash;
@@ -1502,13 +1318,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
// do want to namespace different source file names because they are
// likely different compilations and therefore this would be likely to
// cause cache hits.
- hash.addBytes(main_mod.root_src_path);
- hash.addOptionalBytes(main_mod.root.root_dir.path);
- hash.addBytes(main_mod.root.sub_path);
- {
- var seen_table = std.AutoHashMap(*Package.Module, void).init(arena);
- try addModuleTableToCacheHash(&hash, &arena_allocator, main_mod.deps, &seen_table, .path_bytes);
- }
+ try addModuleTableToCacheHash(gpa, arena, &hash, options.root_mod, .path_bytes);
},
.whole => {
// In this case, we postpone adding the input source file until
@@ -1518,13 +1328,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
}
// Synchronize with other matching comments: ZigOnlyHashStuff
- hash.add(valgrind);
- hash.add(single_threaded);
hash.add(use_llvm);
- hash.add(use_lib_llvm);
+ hash.add(options.config.use_lib_llvm);
hash.add(dll_export_fns);
- hash.add(options.is_test);
- hash.add(options.test_evented_io);
+ hash.add(options.config.is_test);
+ hash.add(options.config.test_evented_io);
hash.addOptionalBytes(options.test_filter);
hash.addOptionalBytes(options.test_name_prefix);
hash.add(options.skip_linker_dependencies);
@@ -1565,85 +1373,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}),
};
- const builtin_mod = try Package.Module.create(arena, .{
- .root = .{ .root_dir = zig_cache_artifact_directory },
- .root_src_path = "builtin.zig",
- .fully_qualified_name = "builtin",
- });
-
- // When you're testing std, the main module is std. In that case,
- // we'll just set the std module to the main one, since avoiding
- // the errors caused by duplicating it is more effort than it's
- // worth.
- const main_mod_is_std = m: {
- const std_path = try std.fs.path.resolve(arena, &[_][]const u8{
- options.zig_lib_directory.path orelse ".",
- "std",
- "std.zig",
- });
- const main_path = try std.fs.path.resolve(arena, &[_][]const u8{
- main_mod.root.root_dir.path orelse ".",
- main_mod.root.sub_path,
- main_mod.root_src_path,
- });
- break :m mem.eql(u8, main_path, std_path);
- };
-
- const std_mod = if (main_mod_is_std)
- main_mod
- else
- try Package.Module.create(arena, .{
- .root = .{
- .root_dir = options.zig_lib_directory,
- .sub_path = "std",
- },
- .root_src_path = "std.zig",
- .fully_qualified_name = "std",
- });
-
- const root_mod = if (options.is_test) root_mod: {
- const test_mod = if (options.test_runner_path) |test_runner| test_mod: {
- const pkg = try Package.Module.create(arena, .{
- .root = .{
- .root_dir = Directory.cwd(),
- .sub_path = std.fs.path.dirname(test_runner) orelse "",
- },
- .root_src_path = std.fs.path.basename(test_runner),
- .fully_qualified_name = "root",
- });
-
- pkg.deps = try main_mod.deps.clone(arena);
- break :test_mod pkg;
- } else try Package.Module.create(arena, .{
- .root = .{
- .root_dir = options.zig_lib_directory,
- },
- .root_src_path = "test_runner.zig",
- .fully_qualified_name = "root",
- });
-
- break :root_mod test_mod;
- } else main_mod;
-
- const compiler_rt_mod = if (include_compiler_rt and options.output_mode == .Obj) compiler_rt_mod: {
- break :compiler_rt_mod try Package.Module.create(arena, .{
- .root = .{
- .root_dir = options.zig_lib_directory,
- },
- .root_src_path = "compiler_rt.zig",
- .fully_qualified_name = "compiler_rt",
- });
- } else null;
-
- {
- try main_mod.deps.ensureUnusedCapacity(arena, 4);
- main_mod.deps.putAssumeCapacity("builtin", builtin_mod);
- main_mod.deps.putAssumeCapacity("root", root_mod);
- main_mod.deps.putAssumeCapacity("std", std_mod);
- if (compiler_rt_mod) |m|
- main_mod.deps.putAssumeCapacity("compiler_rt", m);
- }
-
// Pre-open the directory handles for cached ZIR code so that it does not need
// to redundantly happen for each AstGen operation.
const zir_sub_dir = "z";
@@ -1674,13 +1403,14 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
// However we currently do not have serialization of such metadata, so for now
// we set up an empty Module that does the entire compilation fresh.
- const module = try arena.create(Module);
- errdefer module.deinit();
- module.* = .{
+ const zcu = try arena.create(Module);
+ errdefer zcu.deinit();
+ zcu.* = .{
.gpa = gpa,
.comp = comp,
- .main_mod = main_mod,
- .root_mod = root_mod,
+ .main_mod = options.main_mod orelse options.root_mod,
+ .root_mod = options.root_mod,
+ .std_mod = options.std_mod,
.zig_cache_artifact_directory = zig_cache_artifact_directory,
.global_zir_cache = global_zir_cache,
.local_zir_cache = local_zir_cache,
@@ -1688,31 +1418,24 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.tmp_hack_arena = std.heap.ArenaAllocator.init(gpa),
.error_limit = error_limit,
};
- try module.init();
+ try zcu.init();
- break :blk module;
+ break :blk zcu;
} else blk: {
if (options.emit_h != null) return error.NoZigModuleForCHeader;
break :blk null;
};
- errdefer if (module) |zm| zm.deinit();
-
- const error_return_tracing = !strip and switch (options.optimize_mode) {
- .Debug, .ReleaseSafe => (!options.target.isWasm() or options.target.os.tag == .emscripten) and
- !options.target.cpu.arch.isBpf() and (options.error_tracing orelse true),
- .ReleaseFast => options.error_tracing orelse false,
- .ReleaseSmall => false,
- };
+ errdefer if (zcu) |u| u.deinit();
// For resource management purposes.
var owned_link_dir: ?std.fs.Dir = null;
errdefer if (owned_link_dir) |*dir| dir.close();
- const bin_file_emit: ?link.Emit = blk: {
+ const bin_file_emit: ?Emit = blk: {
const emit_bin = options.emit_bin orelse break :blk null;
if (emit_bin.directory) |directory| {
- break :blk link.Emit{
+ break :blk Emit{
.directory = directory,
.sub_path = emit_bin.basename,
};
@@ -1725,9 +1448,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.incremental => {},
}
- if (module) |zm| {
- break :blk link.Emit{
- .directory = zm.zig_cache_artifact_directory,
+ if (zcu) |u| {
+ break :blk Emit{
+ .directory = u.zig_cache_artifact_directory,
.sub_path = emit_bin.basename,
};
}
@@ -1752,17 +1475,17 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.handle = artifact_dir,
.path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}),
};
- break :blk link.Emit{
+ break :blk Emit{
.directory = link_artifact_directory,
.sub_path = emit_bin.basename,
};
};
- const implib_emit: ?link.Emit = blk: {
+ const implib_emit: ?Emit = blk: {
const emit_implib = options.emit_implib orelse break :blk null;
if (emit_implib.directory) |directory| {
- break :blk link.Emit{
+ break :blk Emit{
.directory = directory,
.sub_path = emit_implib.basename,
};
@@ -1776,13 +1499,13 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
// Use the same directory as the bin. The CLI already emits an
// error if -fno-emit-bin is combined with -femit-implib.
- break :blk link.Emit{
+ break :blk Emit{
.directory = bin_file_emit.?.directory,
.sub_path = emit_implib.basename,
};
};
- const docs_emit: ?link.Emit = blk: {
+ const docs_emit: ?Emit = blk: {
const emit_docs = options.emit_docs orelse break :blk null;
if (emit_docs.directory) |directory| {
@@ -1805,7 +1528,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
};
break :blk .{
- .directory = module.?.zig_cache_artifact_directory,
+ .directory = zcu.?.zig_cache_artifact_directory,
.sub_path = emit_docs.basename,
};
};
@@ -1828,131 +1551,20 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
system_libs.putAssumeCapacity(lib_name, options.system_lib_infos[i]);
}
- const bin_file = try link.File.openPath(gpa, .{
- .emit = bin_file_emit,
- .implib_emit = implib_emit,
- .docs_emit = docs_emit,
- .root_name = root_name,
- .module = module,
- .target = options.target,
- .sysroot = sysroot,
- .output_mode = options.output_mode,
- .link_mode = link_mode,
- .optimize_mode = options.optimize_mode,
- .use_lld = use_lld,
- .use_llvm = use_llvm,
- .use_lib_llvm = use_lib_llvm,
- .link_libc = link_libc,
- .link_libcpp = link_libcpp,
- .link_libunwind = link_libunwind,
- .darwin_sdk_layout = libc_dirs.darwin_sdk_layout,
- .objects = options.link_objects,
- .frameworks = options.frameworks,
- .framework_dirs = options.framework_dirs,
- .system_libs = system_libs,
- .wasi_emulated_libs = options.wasi_emulated_libs,
- .lib_dirs = options.lib_dirs,
- .rpath_list = options.rpath_list,
- .symbol_wrap_set = options.symbol_wrap_set,
- .strip = strip,
- .is_native_os = options.is_native_os,
- .is_native_abi = options.is_native_abi,
- .function_sections = options.function_sections,
- .data_sections = options.data_sections,
- .no_builtin = options.no_builtin,
- .allow_shlib_undefined = options.linker_allow_shlib_undefined,
- .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false,
- .compress_debug_sections = options.linker_compress_debug_sections orelse .none,
- .module_definition_file = options.linker_module_definition_file,
- .sort_section = options.linker_sort_section,
- .import_memory = options.linker_import_memory orelse false,
- .export_memory = options.linker_export_memory orelse !(options.linker_import_memory orelse false),
- .import_symbols = options.linker_import_symbols,
- .import_table = options.linker_import_table,
- .export_table = options.linker_export_table,
- .initial_memory = options.linker_initial_memory,
- .max_memory = options.linker_max_memory,
- .shared_memory = options.linker_shared_memory,
- .global_base = options.linker_global_base,
- .export_symbol_names = options.linker_export_symbol_names,
- .print_gc_sections = options.linker_print_gc_sections,
- .print_icf_sections = options.linker_print_icf_sections,
- .print_map = options.linker_print_map,
- .opt_bisect_limit = options.linker_opt_bisect_limit,
- .z_nodelete = options.linker_z_nodelete,
- .z_notext = options.linker_z_notext,
- .z_defs = options.linker_z_defs,
- .z_origin = options.linker_z_origin,
- .z_nocopyreloc = options.linker_z_nocopyreloc,
- .z_now = options.linker_z_now,
- .z_relro = options.linker_z_relro,
- .z_common_page_size = options.linker_z_common_page_size,
- .z_max_page_size = options.linker_z_max_page_size,
- .tsaware = options.linker_tsaware,
- .nxcompat = options.linker_nxcompat,
- .dynamicbase = options.linker_dynamicbase,
- .linker_optimization = linker_optimization,
- .major_subsystem_version = options.major_subsystem_version,
- .minor_subsystem_version = options.minor_subsystem_version,
- .entry = options.entry,
- .stack_size_override = options.stack_size_override,
- .image_base_override = options.image_base_override,
- .include_compiler_rt = include_compiler_rt,
- .linker_script = options.linker_script,
- .version_script = options.version_script,
- .gc_sections = options.linker_gc_sections,
- .eh_frame_hdr = link_eh_frame_hdr,
- .emit_relocs = options.link_emit_relocs,
- .rdynamic = options.rdynamic,
- .soname = options.soname,
- .version = options.version,
- .compatibility_version = options.compatibility_version,
- .libc_installation = libc_dirs.libc_installation,
- .pic = pic,
- .pie = pie,
- .lto = lto,
- .valgrind = valgrind,
- .tsan = tsan,
- .stack_check = stack_check,
- .stack_protector = stack_protector,
- .red_zone = red_zone,
- .omit_frame_pointer = omit_frame_pointer,
- .single_threaded = single_threaded,
- .verbose_link = options.verbose_link,
- .machine_code_model = options.machine_code_model,
- .dll_export_fns = dll_export_fns,
- .error_return_tracing = error_return_tracing,
- .llvm_cpu_features = llvm_cpu_features,
- .skip_linker_dependencies = options.skip_linker_dependencies,
- .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os,
- .build_id = build_id,
- .cache_mode = cache_mode,
- .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole,
- .subsystem = options.subsystem,
- .is_test = options.is_test,
- .dwarf_format = options.dwarf_format,
- .wasi_exec_model = wasi_exec_model,
- .hash_style = options.hash_style,
- .enable_link_snapshots = options.enable_link_snapshots,
- .install_name = options.install_name,
- .entitlements = options.entitlements,
- .pagezero_size = options.pagezero_size,
- .headerpad_size = options.headerpad_size,
- .headerpad_max_install_names = options.headerpad_max_install_names,
- .dead_strip_dylibs = options.dead_strip_dylibs,
- .force_undefined_symbols = options.force_undefined_symbols,
- .pdb_source_path = options.pdb_source_path,
- .pdb_out_path = options.pdb_out_path,
- .want_structured_cfg = options.want_structured_cfg,
- });
- errdefer bin_file.destroy();
+ const each_lib_rpath = options.each_lib_rpath orelse
+ options.root_mod.resolved_target.is_native_os;
+
comp.* = .{
.gpa = gpa,
.arena = arena_allocator,
+ .module = zcu,
+ .root_mod = options.root_mod,
+ .config = options.config,
+ .bin_file = null,
+ .cache_mode = cache_mode,
.zig_lib_directory = options.zig_lib_directory,
.local_cache_directory = options.local_cache_directory,
.global_cache_directory = options.global_cache_directory,
- .bin_file = bin_file,
.whole_bin_sub_path = whole_bin_sub_path,
.whole_implib_sub_path = whole_implib_sub_path,
.whole_docs_sub_path = whole_docs_sub_path,
@@ -1966,8 +1578,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.astgen_work_queue = std.fifo.LinearFifo(*Module.File, .Dynamic).init(gpa),
.embed_file_work_queue = std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic).init(gpa),
.keep_source_files_loaded = options.keep_source_files_loaded,
- .c_frontend = c_frontend,
- .clang_argv = options.clang_argv,
.c_source_files = options.c_source_files,
.rc_source_files = options.rc_source_files,
.cache_parent = cache,
@@ -1975,7 +1585,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.libc_include_dir_list = libc_dirs.libc_include_dir_list,
.libc_framework_dir_list = libc_dirs.libc_framework_dir_list,
.rc_include_dir_list = rc_dirs.libc_include_dir_list,
- .sanitize_c = sanitize_c,
.thread_pool = options.thread_pool,
.clang_passthrough_mode = options.clang_passthrough_mode,
.clang_preprocessor_mode = options.clang_preprocessor_mode,
@@ -1994,22 +1603,110 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.formatted_panics = formatted_panics,
.time_report = options.time_report,
.stack_report = options.stack_report,
- .unwind_tables = unwind_tables,
.test_filter = options.test_filter,
.test_name_prefix = options.test_name_prefix,
- .test_evented_io = options.test_evented_io,
.debug_compiler_runtime_libs = options.debug_compiler_runtime_libs,
.debug_compile_errors = options.debug_compile_errors,
.libcxx_abi_version = options.libcxx_abi_version,
+ .implib_emit = implib_emit,
+ .docs_emit = docs_emit,
+ .root_name = root_name,
+ .sysroot = sysroot,
+ .system_libs = system_libs,
+ .version = options.version,
+ .libc_installation = libc_dirs.libc_installation,
+ .include_compiler_rt = include_compiler_rt,
+ .objects = options.link_objects,
};
+
+ if (bin_file_emit) |emit| {
+ comp.bin_file = try link.File.open(arena, .{
+ .comp = comp,
+ .emit = emit,
+ .optimization = linker_optimization,
+ .linker_script = options.linker_script,
+ .z_nodelete = options.linker_z_nodelete,
+ .z_notext = options.linker_z_notext,
+ .z_defs = options.linker_z_defs,
+ .z_origin = options.linker_z_origin,
+ .z_nocopyreloc = options.linker_z_nocopyreloc,
+ .z_now = options.linker_z_now,
+ .z_relro = options.linker_z_relro,
+ .z_common_page_size = options.linker_z_common_page_size,
+ .z_max_page_size = options.linker_z_max_page_size,
+ .darwin_sdk_layout = libc_dirs.darwin_sdk_layout,
+ .frameworks = options.frameworks,
+ .framework_dirs = options.framework_dirs,
+ .wasi_emulated_libs = options.wasi_emulated_libs,
+ .lib_dirs = options.lib_dirs,
+ .rpath_list = options.rpath_list,
+ .symbol_wrap_set = options.symbol_wrap_set,
+ .function_sections = options.function_sections,
+ .data_sections = options.data_sections,
+ .no_builtin = options.no_builtin,
+ .allow_shlib_undefined = options.linker_allow_shlib_undefined,
+ .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false,
+ .compress_debug_sections = options.linker_compress_debug_sections orelse .none,
+ .module_definition_file = options.linker_module_definition_file,
+ .sort_section = options.linker_sort_section,
+ .import_symbols = options.linker_import_symbols,
+ .import_table = options.linker_import_table,
+ .export_table = options.linker_export_table,
+ .initial_memory = options.linker_initial_memory,
+ .max_memory = options.linker_max_memory,
+ .global_base = options.linker_global_base,
+ .export_symbol_names = options.linker_export_symbol_names,
+ .print_gc_sections = options.linker_print_gc_sections,
+ .print_icf_sections = options.linker_print_icf_sections,
+ .print_map = options.linker_print_map,
+ .opt_bisect_limit = options.linker_opt_bisect_limit,
+ .tsaware = options.linker_tsaware,
+ .nxcompat = options.linker_nxcompat,
+ .dynamicbase = options.linker_dynamicbase,
+ .major_subsystem_version = options.major_subsystem_version,
+ .minor_subsystem_version = options.minor_subsystem_version,
+ .stack_size_override = options.stack_size_override,
+ .image_base_override = options.image_base_override,
+ .version_script = options.version_script,
+ .gc_sections = options.linker_gc_sections,
+ .eh_frame_hdr = link_eh_frame_hdr,
+ .emit_relocs = options.link_emit_relocs,
+ .rdynamic = options.rdynamic,
+ .soname = options.soname,
+ .compatibility_version = options.compatibility_version,
+ .verbose_link = options.verbose_link,
+ .dll_export_fns = dll_export_fns,
+ .skip_linker_dependencies = options.skip_linker_dependencies,
+ .parent_compilation_link_libc = options.parent_compilation_link_libc,
+ .each_lib_rpath = each_lib_rpath,
+ .build_id = build_id,
+ .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole,
+ .subsystem = options.subsystem,
+ .dwarf_format = options.dwarf_format,
+ .hash_style = options.hash_style,
+ .enable_link_snapshots = options.enable_link_snapshots,
+ .install_name = options.install_name,
+ .entitlements = options.entitlements,
+ .pagezero_size = options.pagezero_size,
+ .headerpad_size = options.headerpad_size,
+ .headerpad_max_install_names = options.headerpad_max_install_names,
+ .dead_strip_dylibs = options.dead_strip_dylibs,
+ .force_undefined_symbols = options.force_undefined_symbols,
+ .pdb_source_path = options.pdb_source_path,
+ .pdb_out_path = options.pdb_out_path,
+ .want_structured_cfg = options.want_structured_cfg,
+ .entry_addr = null, // CLI does not expose this option (yet?)
+ });
+ }
+
break :comp comp;
};
errdefer comp.destroy();
- const target = comp.getTarget();
+ const target = options.root_mod.resolved_target.result;
- const capable_of_building_compiler_rt = canBuildLibCompilerRt(target, comp.bin_file.options.use_llvm);
- const capable_of_building_zig_libc = canBuildZigLibC(target, comp.bin_file.options.use_llvm);
+ const capable_of_building_compiler_rt = canBuildLibCompilerRt(target, options.config.use_llvm);
+ const capable_of_building_zig_libc = canBuildZigLibC(target, options.config.use_llvm);
// Add a `CObject` for each `c_source_files`.
try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len);
@@ -2109,7 +1806,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
});
}
comp.work_queue.writeAssumeCapacity(&[_]Job{
- .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(wasi_exec_model) },
+ .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(options.config.wasi_exec_model) },
.{ .wasi_libc_crt_file = .libc_a },
});
}
@@ -2171,7 +1868,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
if (is_exe_or_dyn_lib) {
log.debug("queuing a job to build compiler_rt_lib", .{});
comp.job_queued_compiler_rt_lib = true;
- } else if (options.output_mode != .Obj) {
+ } else if (output_mode != .Obj) {
log.debug("queuing a job to build compiler_rt_obj", .{});
// In this case we are making a static library, so we ask
// for a compiler-rt object to put in it.
@@ -2283,7 +1980,7 @@ pub fn clearMiscFailures(comp: *Compilation) void {
}
pub fn getTarget(self: Compilation) Target {
- return self.bin_file.options.target;
+ return self.root_mod.resolved_target.result;
}
fn restorePrevZigCacheArtifactDirectory(comp: *Compilation, directory: *Directory) void {
@@ -2436,7 +2133,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
// Make sure std.zig is inside the import_table. We unconditionally need
// it for start.zig.
- const std_mod = module.main_mod.deps.get("std").?;
+ const std_mod = module.std_mod;
_ = try module.importPkg(std_mod);
// Normally we rely on importing std to in turn import the root source file
@@ -2449,7 +2146,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
_ = try module.importPkg(module.main_mod);
}
- if (module.main_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
+ if (module.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
_ = try module.importPkg(compiler_rt_mod);
}
@@ -2474,7 +2171,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
try comp.work_queue.writeItem(.{ .analyze_mod = module.main_mod });
}
- if (module.main_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
+ if (module.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
try comp.work_queue.writeItem(.{ .analyze_mod = compiler_rt_mod });
}
}
@@ -2699,16 +2396,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
if (comp.bin_file.options.module) |mod| {
const main_zig_file = try mod.main_mod.root.joinString(arena, mod.main_mod.root_src_path);
_ = try man.addFile(main_zig_file, null);
- {
- var seen_table = std.AutoHashMap(*Package.Module, void).init(arena);
-
- // Skip builtin.zig; it is useless as an input, and we don't want to have to
- // write it before checking for a cache hit.
- const builtin_mod = mod.main_mod.deps.get("builtin").?;
- try seen_table.put(builtin_mod, {});
-
- try addModuleTableToCacheHash(&man.hash, &arena_allocator, mod.main_mod.deps, &seen_table, .{ .files = man });
- }
+ try addModuleTableToCacheHash(gpa, arena, &man.hash, mod.main_mod, .{ .files = man });
// Synchronize with other matching comments: ZigOnlyHashStuff
man.hash.add(comp.bin_file.options.valgrind);
@@ -2762,8 +2450,6 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);
cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc);
- man.hash.addListOfBytes(comp.clang_argv);
-
man.hash.addOptional(comp.bin_file.options.stack_size_override);
man.hash.addOptional(comp.bin_file.options.image_base_override);
man.hash.addOptional(comp.bin_file.options.gc_sections);
@@ -3341,7 +3027,7 @@ pub const ErrorNoteHashContext = struct {
const eb = ctx.eb.tmpBundle();
const msg_a = eb.nullTerminatedString(a.msg);
const msg_b = eb.nullTerminatedString(b.msg);
- if (!std.mem.eql(u8, msg_a, msg_b)) return false;
+ if (!mem.eql(u8, msg_a, msg_b)) return false;
if (a.src_loc == .none and b.src_loc == .none) return true;
if (a.src_loc == .none or b.src_loc == .none) return false;
@@ -3351,7 +3037,7 @@ pub const ErrorNoteHashContext = struct {
const src_path_a = eb.nullTerminatedString(src_a.src_path);
const src_path_b = eb.nullTerminatedString(src_b.src_path);
- return std.mem.eql(u8, src_path_a, src_path_b) and
+ return mem.eql(u8, src_path_a, src_path_b) and
src_a.line == src_b.line and
src_a.column == src_b.column and
src_a.span_main == src_b.span_main;
@@ -4149,7 +3835,7 @@ pub const CImportResult = struct {
cache_hit: bool,
errors: std.zig.ErrorBundle,
- pub fn deinit(result: *CImportResult, gpa: std.mem.Allocator) void {
+ pub fn deinit(result: *CImportResult, gpa: mem.Allocator) void {
result.errors.deinit(gpa);
}
};
@@ -5321,9 +5007,9 @@ pub fn addCCArgs(
argv.appendAssumeCapacity(arg);
}
}
- const mcmodel = comp.bin_file.options.machine_code_model;
- if (mcmodel != .default) {
- try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={s}", .{@tagName(mcmodel)}));
+ const code_model = comp.bin_file.options.machine_code_model;
+ if (code_model != .default) {
+ try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={s}", .{@tagName(code_model)}));
}
switch (target.os.tag) {
@@ -5618,68 +5304,6 @@ fn failCObjWithOwnedDiagBundle(
return error.AnalysisFail;
}
-/// The include directories used when preprocessing .rc files are separate from the
-/// target. Which include directories are used is determined by `options.rc_includes`.
-///
-/// Note: It should be okay that the include directories used when compiling .rc
-/// files differ from the include directories used when compiling the main
-/// binary, since the .res format is not dependent on anything ABI-related. The
-/// only relevant differences would be things like `#define` constants being
-/// different in the MinGW headers vs the MSVC headers, but any such
-/// differences would likely be a MinGW bug.
-fn detectWin32ResourceIncludeDirs(arena: Allocator, options: InitOptions) !LibCDirs {
- // Set the includes to .none here when there are no rc files to compile
- var includes = if (options.rc_source_files.len > 0) options.rc_includes else .none;
- if (builtin.target.os.tag != .windows) {
- switch (includes) {
- // MSVC can't be found when the host isn't Windows, so short-circuit.
- .msvc => return error.WindowsSdkNotFound,
- // Skip straight to gnu since we won't be able to detect MSVC on non-Windows hosts.
- .any => includes = .gnu,
- .none, .gnu => {},
- }
- }
- while (true) {
- switch (includes) {
- .any, .msvc => return detectLibCIncludeDirs(
- arena,
- options.zig_lib_directory.path.?,
- .{
- .cpu = options.target.cpu,
- .os = options.target.os,
- .abi = .msvc,
- .ofmt = options.target.ofmt,
- },
- options.is_native_abi,
- // The .rc preprocessor will need to know the libc include dirs even if we
- // are not linking libc, so force 'link_libc' to true
- true,
- options.libc_installation,
- ) catch |err| {
- if (includes == .any) {
- // fall back to mingw
- includes = .gnu;
- continue;
- }
- return err;
- },
- .gnu => return detectLibCFromBuilding(arena, options.zig_lib_directory.path.?, .{
- .cpu = options.target.cpu,
- .os = options.target.os,
- .abi = .gnu,
- .ofmt = options.target.ofmt,
- }),
- .none => return LibCDirs{
- .libc_include_dir_list = &[0][]u8{},
- .libc_installation = null,
- .libc_framework_dir_list = &.{},
- .sysroot = null,
- .darwin_sdk_layout = null,
- },
- }
- }
-}
-
fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) SemaError {
@setCold(true);
var bundle: ErrorBundle.Wip = undefined;
@@ -6061,7 +5685,7 @@ const LibCDirs = struct {
libc_installation: ?*const LibCInstallation,
libc_framework_dir_list: []const []const u8,
sysroot: ?[]const u8,
- darwin_sdk_layout: ?link.DarwinSdkLayout,
+ darwin_sdk_layout: ?link.File.MachO.SdkLayout,
};
fn getZigShippedLibCIncludeDirsDarwin(arena: Allocator, zig_lib_dir: []const u8) !LibCDirs {
@@ -6374,7 +5998,7 @@ fn parseLldStderr(comp: *Compilation, comptime prefix: []const u8, stderr: []con
err.context_lines = try context_lines.toOwnedSlice();
}
- var split = std.mem.splitSequence(u8, line, "error: ");
+ var split = mem.splitSequence(u8, line, "error: ");
_ = split.first();
const duped_msg = try std.fmt.allocPrint(comp.gpa, "{s}: {s}", .{ prefix, split.rest() });
@@ -6427,7 +6051,7 @@ fn canBuildLibCompilerRt(target: std.Target, use_llvm: bool) bool {
.spirv32, .spirv64 => return false,
else => {},
}
- return switch (zigBackend(target, use_llvm)) {
+ return switch (target_util.zigBackend(target, use_llvm)) {
.stage2_llvm => true,
.stage2_x86_64 => if (target.ofmt == .elf) true else build_options.have_llvm,
else => build_options.have_llvm,
@@ -6445,7 +6069,7 @@ fn canBuildZigLibC(target: std.Target, use_llvm: bool) bool {
.spirv32, .spirv64 => return false,
else => {},
}
- return switch (zigBackend(target, use_llvm)) {
+ return switch (target_util.zigBackend(target, use_llvm)) {
.stage2_llvm => true,
.stage2_x86_64 => if (target.ofmt == .elf) true else build_options.have_llvm,
else => build_options.have_llvm,
@@ -6454,236 +6078,7 @@ fn canBuildZigLibC(target: std.Target, use_llvm: bool) bool {
pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend {
const target = comp.bin_file.options.target;
- return zigBackend(target, comp.bin_file.options.use_llvm);
-}
-
-fn zigBackend(target: std.Target, use_llvm: bool) std.builtin.CompilerBackend {
- if (use_llvm) return .stage2_llvm;
- if (target.ofmt == .c) return .stage2_c;
- return switch (target.cpu.arch) {
- .wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm,
- .arm, .armeb, .thumb, .thumbeb => .stage2_arm,
- .x86_64 => .stage2_x86_64,
- .x86 => .stage2_x86,
- .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64,
- .riscv64 => .stage2_riscv64,
- .sparc64 => .stage2_sparc64,
- .spirv64 => .stage2_spirv64,
- else => .other,
- };
-}
-
-pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Allocator.Error![:0]u8 {
- const tracy_trace = trace(@src());
- defer tracy_trace.end();
-
- var buffer = std.ArrayList(u8).init(allocator);
- defer buffer.deinit();
-
- const target = comp.getTarget();
- const generic_arch_name = target.cpu.arch.genericName();
- const zig_backend = comp.getZigBackend();
-
- @setEvalBranchQuota(4000);
- try buffer.writer().print(
- \\const std = @import("std");
- \\/// Zig version. When writing code that supports multiple versions of Zig, prefer
- \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.
- \\pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable;
- \\pub const zig_version_string = "{s}";
- \\pub const zig_backend = std.builtin.CompilerBackend.{};
- \\
- \\pub const output_mode = std.builtin.OutputMode.{};
- \\pub const link_mode = std.builtin.LinkMode.{};
- \\pub const is_test = {};
- \\pub const single_threaded = {};
- \\pub const abi = std.Target.Abi.{};
- \\pub const cpu: std.Target.Cpu = .{{
- \\ .arch = .{},
- \\ .model = &std.Target.{}.cpu.{},
- \\ .features = std.Target.{}.featureSet(&[_]std.Target.{}.Feature{{
- \\
- , .{
- build_options.version,
- std.zig.fmtId(@tagName(zig_backend)),
- std.zig.fmtId(@tagName(comp.bin_file.options.output_mode)),
- std.zig.fmtId(@tagName(comp.bin_file.options.link_mode)),
- comp.bin_file.options.is_test,
- comp.bin_file.options.single_threaded,
- std.zig.fmtId(@tagName(target.abi)),
- std.zig.fmtId(@tagName(target.cpu.arch)),
- std.zig.fmtId(generic_arch_name),
- std.zig.fmtId(target.cpu.model.name),
- std.zig.fmtId(generic_arch_name),
- std.zig.fmtId(generic_arch_name),
- });
-
- for (target.cpu.arch.allFeaturesList(), 0..) |feature, index_usize| {
- const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize));
- const is_enabled = target.cpu.features.isEnabled(index);
- if (is_enabled) {
- try buffer.writer().print(" .{},\n", .{std.zig.fmtId(feature.name)});
- }
- }
- try buffer.writer().print(
- \\ }}),
- \\}};
- \\pub const os = std.Target.Os{{
- \\ .tag = .{},
- \\ .version_range = .{{
- ,
- .{std.zig.fmtId(@tagName(target.os.tag))},
- );
-
- switch (target.os.getVersionRange()) {
- .none => try buffer.appendSlice(" .none = {} },\n"),
- .semver => |semver| try buffer.writer().print(
- \\ .semver = .{{
- \\ .min = .{{
- \\ .major = {},
- \\ .minor = {},
- \\ .patch = {},
- \\ }},
- \\ .max = .{{
- \\ .major = {},
- \\ .minor = {},
- \\ .patch = {},
- \\ }},
- \\ }}}},
- \\
- , .{
- semver.min.major,
- semver.min.minor,
- semver.min.patch,
-
- semver.max.major,
- semver.max.minor,
- semver.max.patch,
- }),
- .linux => |linux| try buffer.writer().print(
- \\ .linux = .{{
- \\ .range = .{{
- \\ .min = .{{
- \\ .major = {},
- \\ .minor = {},
- \\ .patch = {},
- \\ }},
- \\ .max = .{{
- \\ .major = {},
- \\ .minor = {},
- \\ .patch = {},
- \\ }},
- \\ }},
- \\ .glibc = .{{
- \\ .major = {},
- \\ .minor = {},
- \\ .patch = {},
- \\ }},
- \\ }}}},
- \\
- , .{
- linux.range.min.major,
- linux.range.min.minor,
- linux.range.min.patch,
-
- linux.range.max.major,
- linux.range.max.minor,
- linux.range.max.patch,
-
- linux.glibc.major,
- linux.glibc.minor,
- linux.glibc.patch,
- }),
- .windows => |windows| try buffer.writer().print(
- \\ .windows = .{{
- \\ .min = {s},
- \\ .max = {s},
- \\ }}}},
- \\
- ,
- .{ windows.min, windows.max },
- ),
- }
- try buffer.appendSlice(
- \\};
- \\pub const target: std.Target = .{
- \\ .cpu = cpu,
- \\ .os = os,
- \\ .abi = abi,
- \\ .ofmt = object_format,
- \\
- );
-
- if (target.dynamic_linker.get()) |dl| {
- try buffer.writer().print(
- \\ .dynamic_linker = std.Target.DynamicLinker.init("{s}"),
- \\}};
- \\
- , .{dl});
- } else {
- try buffer.appendSlice(
- \\ .dynamic_linker = std.Target.DynamicLinker.none,
- \\};
- \\
- );
- }
-
- try buffer.writer().print(
- \\pub const object_format = std.Target.ObjectFormat.{};
- \\pub const mode = std.builtin.OptimizeMode.{};
- \\pub const link_libc = {};
- \\pub const link_libcpp = {};
- \\pub const have_error_return_tracing = {};
- \\pub const valgrind_support = {};
- \\pub const sanitize_thread = {};
- \\pub const position_independent_code = {};
- \\pub const position_independent_executable = {};
- \\pub const strip_debug_info = {};
- \\pub const code_model = std.builtin.CodeModel.{};
- \\pub const omit_frame_pointer = {};
- \\
- , .{
- std.zig.fmtId(@tagName(target.ofmt)),
- std.zig.fmtId(@tagName(comp.bin_file.options.optimize_mode)),
- comp.bin_file.options.link_libc,
- comp.bin_file.options.link_libcpp,
- comp.bin_file.options.error_return_tracing,
- comp.bin_file.options.valgrind,
- comp.bin_file.options.tsan,
- comp.bin_file.options.pic,
- comp.bin_file.options.pie,
- comp.bin_file.options.strip,
- std.zig.fmtId(@tagName(comp.bin_file.options.machine_code_model)),
- comp.bin_file.options.omit_frame_pointer,
- });
-
- if (target.os.tag == .wasi) {
- const wasi_exec_model_fmt = std.zig.fmtId(@tagName(comp.bin_file.options.wasi_exec_model));
- try buffer.writer().print(
- \\pub const wasi_exec_model = std.builtin.WasiExecModel.{};
- \\
- , .{wasi_exec_model_fmt});
- }
-
- if (comp.bin_file.options.is_test) {
- try buffer.appendSlice(
- \\pub var test_functions: []const std.builtin.TestFn = undefined; // overwritten later
- \\
- );
- if (comp.test_evented_io) {
- try buffer.appendSlice(
- \\pub const test_io_mode = .evented;
- \\
- );
- } else {
- try buffer.appendSlice(
- \\pub const test_io_mode = .blocking;
- \\
- );
- }
- }
-
- return buffer.toOwnedSliceSentinel(0);
+ return target_util.zigBackend(target, comp.bin_file.options.use_llvm);
}
pub fn updateSubCompilation(
@@ -6730,21 +6125,46 @@ fn buildOutputFromZig(
const tracy_trace = trace(@src());
defer tracy_trace.end();
+ var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
+ defer arena_allocator.deinit();
+ const arena = arena_allocator.allocator();
+
assert(output_mode != .Exe);
- var main_mod: Package.Module = .{
- .root = .{ .root_dir = comp.zig_lib_directory },
- .root_src_path = src_basename,
+ const config = try Config.resolve(.{
+ .output_mode = output_mode,
+ .resolved_target = comp.root_mod.resolved_target,
+ .is_test = false,
+ .have_zcu = true,
+ .emit_bin = true,
+ .root_optimize_mode = comp.compilerRtOptMode(),
+ });
+
+ const root_mod = Package.Module.create(.{
+ .paths = .{
+ .root = .{ .root_dir = comp.zig_lib_directory },
+ .root_src_path = src_basename,
+ },
.fully_qualified_name = "root",
- };
+ .inherited = .{
+ .strip = comp.compilerRtStrip(),
+ .stack_check = false,
+ .stack_protector = 0,
+ .red_zone = comp.root_mod.red_zone,
+ .omit_frame_pointer = comp.root_mod.omit_frame_pointer,
+ .unwind_tables = comp.bin_file.options.eh_frame_hdr,
+ .pic = comp.root_mod.pic,
+ },
+ .global = config,
+ .cc_argv = &.{},
+ });
const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len];
const target = comp.getTarget();
- const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{
+ const bin_basename = try std.zig.binNameAlloc(arena, .{
.root_name = root_name,
.target = target,
.output_mode = output_mode,
});
- defer comp.gpa.free(bin_basename);
const emit_bin = Compilation.EmitLoc{
.directory = null, // Put it in the cache directory.
@@ -6754,33 +6174,18 @@ fn buildOutputFromZig(
.global_cache_directory = comp.global_cache_directory,
.local_cache_directory = comp.global_cache_directory,
.zig_lib_directory = comp.zig_lib_directory,
+ .resolved = config,
.cache_mode = .whole,
- .target = target,
.root_name = root_name,
- .main_mod = &main_mod,
- .output_mode = output_mode,
+ .root_mod = root_mod,
.thread_pool = comp.thread_pool,
.libc_installation = comp.bin_file.options.libc_installation,
.emit_bin = emit_bin,
- .optimize_mode = comp.compilerRtOptMode(),
.link_mode = .Static,
.function_sections = true,
.data_sections = true,
.no_builtin = true,
- .want_sanitize_c = false,
- .want_stack_check = false,
- .want_stack_protector = 0,
- .want_red_zone = comp.bin_file.options.red_zone,
- .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer,
- .want_valgrind = false,
- .want_tsan = false,
- .want_unwind_tables = comp.bin_file.options.eh_frame_hdr,
- .want_pic = comp.bin_file.options.pic,
- .want_pie = null,
.emit_h = null,
- .strip = comp.compilerRtStrip(),
- .is_native_os = comp.bin_file.options.is_native_os,
- .is_native_abi = comp.bin_file.options.is_native_abi,
.self_exe_path = comp.self_exe_path,
.verbose_cc = comp.verbose_cc,
.verbose_link = comp.bin_file.options.verbose_link,
@@ -6815,7 +6220,7 @@ pub fn build_crt_file(
output_mode: std.builtin.OutputMode,
misc_task_tag: MiscTask,
prog_node: *std.Progress.Node,
- c_source_files: []const Compilation.CSourceFile,
+ c_source_files: []const CSourceFile,
) !void {
const tracy_trace = trace(@src());
defer tracy_trace.end();
src/link.zig
@@ -66,237 +66,18 @@ pub fn hashAddFrameworks(man: *Cache.Manifest, hm: []const Framework) !void {
pub const producer_string = if (builtin.is_test) "zig test" else "zig " ++ build_options.version;
-pub const Emit = struct {
- /// Where the output will go.
- directory: Compilation.Directory,
- /// Path to the output file, relative to `directory`.
- sub_path: []const u8,
-
- /// Returns the full path to `basename` if it were in the same directory as the
- /// `Emit` sub_path.
- pub fn basenamePath(emit: Emit, arena: Allocator, basename: [:0]const u8) ![:0]const u8 {
- const full_path = if (emit.directory.path) |p|
- try fs.path.join(arena, &[_][]const u8{ p, emit.sub_path })
- else
- emit.sub_path;
-
- if (fs.path.dirname(full_path)) |dirname| {
- return try fs.path.joinZ(arena, &.{ dirname, basename });
- } else {
- return basename;
- }
- }
-};
-
-pub const Options = struct {
- /// This is `null` when `-fno-emit-bin` is used.
- emit: ?Emit,
- /// This is `null` when not building a Windows DLL, or when `-fno-emit-implib` is used.
- implib_emit: ?Emit,
- /// This is non-null when `-femit-docs` is provided.
- docs_emit: ?Emit,
- target: std.Target,
- output_mode: std.builtin.OutputMode,
- link_mode: std.builtin.LinkMode,
- optimize_mode: std.builtin.OptimizeMode,
- machine_code_model: std.builtin.CodeModel,
- root_name: [:0]const u8,
- /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
- module: ?*Module,
- /// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin)
- sysroot: ?[]const u8,
- /// Used for calculating how much space to reserve for symbols in case the binary file
- /// does not already have a symbol table.
- symbol_count_hint: u64 = 32,
- /// Used for calculating how much space to reserve for executable program code in case
- /// the binary file does not already have such a section.
- program_code_size_hint: u64 = 256 * 1024,
- entry_addr: ?u64 = null,
- entry: ?[]const u8,
- stack_size_override: ?u64,
- image_base_override: ?u64,
- /// 0 means no stack protector
- /// other value means stack protector with that buffer size.
- stack_protector: u32,
- cache_mode: CacheMode,
- include_compiler_rt: bool,
- /// Set to `true` to omit debug info.
- strip: bool,
- /// If this is true then this link code is responsible for outputting an object
- /// file and then using LLD to link it together with the link options and other objects.
- /// Otherwise (depending on `use_llvm`) this link code directly outputs and updates the final binary.
- use_lld: bool,
- /// If this is true then this link code is responsible for making an LLVM IR Module,
- /// outputting it to an object file, and then linking that together with link options and
- /// other objects.
- /// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary.
- use_llvm: bool,
- use_lib_llvm: bool,
- link_libc: bool,
- link_libcpp: bool,
- link_libunwind: bool,
- darwin_sdk_layout: ?DarwinSdkLayout,
- function_sections: bool,
- data_sections: bool,
- no_builtin: bool,
- eh_frame_hdr: bool,
- emit_relocs: bool,
- rdynamic: bool,
- z_nodelete: bool,
- z_notext: bool,
- z_defs: bool,
- z_origin: bool,
- z_nocopyreloc: bool,
- z_now: bool,
- z_relro: bool,
- z_common_page_size: ?u64,
- z_max_page_size: ?u64,
- tsaware: bool,
- nxcompat: bool,
- dynamicbase: bool,
- linker_optimization: u8,
- compress_debug_sections: CompressDebugSections,
- bind_global_refs_locally: bool,
- import_memory: bool,
- export_memory: bool,
- import_symbols: bool,
- import_table: bool,
- export_table: bool,
- initial_memory: ?u64,
- max_memory: ?u64,
- shared_memory: bool,
- export_symbol_names: []const []const u8,
- global_base: ?u64,
- is_native_os: bool,
- is_native_abi: bool,
- pic: bool,
- pie: bool,
- lto: bool,
- valgrind: bool,
- tsan: bool,
- stack_check: bool,
- red_zone: bool,
- omit_frame_pointer: bool,
- single_threaded: bool,
- verbose_link: bool,
- dll_export_fns: bool,
- error_return_tracing: bool,
- skip_linker_dependencies: bool,
- each_lib_rpath: bool,
- build_id: std.zig.BuildId,
- disable_lld_caching: bool,
- is_test: bool,
- hash_style: HashStyle,
- sort_section: ?SortSection,
- major_subsystem_version: ?u32,
- minor_subsystem_version: ?u32,
- gc_sections: ?bool = null,
- allow_shlib_undefined: ?bool,
- subsystem: ?std.Target.SubSystem,
- linker_script: ?[]const u8,
- version_script: ?[]const u8,
- soname: ?[]const u8,
- llvm_cpu_features: ?[*:0]const u8,
- print_gc_sections: bool,
- print_icf_sections: bool,
- print_map: bool,
- opt_bisect_limit: i32,
-
- objects: []Compilation.LinkObject,
- framework_dirs: []const []const u8,
- frameworks: []const Framework,
- /// These are *always* dynamically linked. Static libraries will be
- /// provided as positional arguments.
- system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
- wasi_emulated_libs: []const wasi_libc.CRTFile,
- // TODO: remove this. libraries are resolved by the frontend.
- lib_dirs: []const []const u8,
- rpath_list: []const []const u8,
-
- /// List of symbols forced as undefined in the symbol table
- /// thus forcing their resolution by the linker.
- /// Corresponds to `-u <symbol>` for ELF/MachO and `/include:<symbol>` for COFF/PE.
- force_undefined_symbols: std.StringArrayHashMapUnmanaged(void),
- /// Use a wrapper function for symbol. Any undefined reference to symbol
- /// will be resolved to __wrap_symbol. Any undefined reference to
- /// __real_symbol will be resolved to symbol. This can be used to provide a
- /// wrapper for a system function. The wrapper function should be called
- /// __wrap_symbol. If it wishes to call the system function, it should call
- /// __real_symbol.
- symbol_wrap_set: std.StringArrayHashMapUnmanaged(void),
-
- version: ?std.SemanticVersion,
- compatibility_version: ?std.SemanticVersion,
- libc_installation: ?*const LibCInstallation,
-
- dwarf_format: ?std.dwarf.Format,
-
- /// WASI-only. Type of WASI execution model ("command" or "reactor").
- wasi_exec_model: std.builtin.WasiExecModel = undefined,
-
- /// (Zig compiler development) Enable dumping of linker's state as JSON.
- enable_link_snapshots: bool = false,
-
- /// (Darwin) Install name for the dylib
- install_name: ?[]const u8 = null,
-
- /// (Darwin) Path to entitlements file
- entitlements: ?[]const u8 = null,
-
- /// (Darwin) size of the __PAGEZERO segment
- pagezero_size: ?u64 = null,
-
- /// (Darwin) set minimum space for future expansion of the load commands
- headerpad_size: ?u32 = null,
-
- /// (Darwin) set enough space as if all paths were MATPATHLEN
- headerpad_max_install_names: bool = false,
-
- /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
- dead_strip_dylibs: bool = false,
-
- /// (Windows) PDB source path prefix to instruct the linker how to resolve relative
- /// paths when consolidating CodeView streams into a single PDB file.
- pdb_source_path: ?[]const u8 = null,
-
- /// (Windows) PDB output path
- pdb_out_path: ?[]const u8 = null,
-
- /// (Windows) .def file to specify when linking
- module_definition_file: ?[]const u8 = null,
-
- /// (SPIR-V) whether to generate a structured control flow graph or not
- want_structured_cfg: ?bool = null,
-
- pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
- return if (options.use_lld) .Obj else options.output_mode;
- }
-
- pub fn move(self: *Options) Options {
- const copied_state = self.*;
- self.system_libs = .{};
- self.force_undefined_symbols = .{};
- return copied_state;
- }
-};
-
pub const HashStyle = enum { sysv, gnu, both };
pub const CompressDebugSections = enum { none, zlib, zstd };
-/// The filesystem layout of darwin SDK elements.
-pub const DarwinSdkLayout = enum {
- /// macOS SDK layout: TOP { /usr/include, /usr/lib, /System/Library/Frameworks }.
- sdk,
- /// Shipped libc layout: TOP { /lib/libc/include, /lib/libc/darwin, <NONE> }.
- vendored,
-};
-
pub const File = struct {
tag: Tag,
- options: Options,
+
+ /// The owner of this output File.
+ comp: *Compilation,
+ emit: Compilation.Emit,
+
file: ?fs.File,
- allocator: Allocator,
/// When linking with LLD, this linker code will output an object file only at
/// this location, and then this path can be placed on the LLD linker line.
intermediary_basename: ?[]const u8 = null,
@@ -307,103 +88,132 @@ pub const File = struct {
child_pid: ?std.ChildProcess.Id = null,
+ pub const OpenOptions = struct {
+ comp: *Compilation,
+ emit: Compilation.Emit,
+
+ symbol_count_hint: u64 = 32,
+ program_code_size_hint: u64 = 256 * 1024,
+
+ /// Virtual address of the entry point procedure relative to image base.
+ entry_addr: ?u64,
+ stack_size_override: ?u64,
+ image_base_override: ?u64,
+ function_sections: bool,
+ data_sections: bool,
+ no_builtin: bool,
+ eh_frame_hdr: bool,
+ emit_relocs: bool,
+ rdynamic: bool,
+ optimization: u8,
+ linker_script: ?[]const u8,
+ z_nodelete: bool,
+ z_notext: bool,
+ z_defs: bool,
+ z_origin: bool,
+ z_nocopyreloc: bool,
+ z_now: bool,
+ z_relro: bool,
+ z_common_page_size: ?u64,
+ z_max_page_size: ?u64,
+ tsaware: bool,
+ nxcompat: bool,
+ dynamicbase: bool,
+ compress_debug_sections: CompressDebugSections,
+ bind_global_refs_locally: bool,
+ import_symbols: bool,
+ import_table: bool,
+ export_table: bool,
+ initial_memory: ?u64,
+ max_memory: ?u64,
+ export_symbol_names: []const []const u8,
+ global_base: ?u64,
+ verbose_link: bool,
+ dll_export_fns: bool,
+ skip_linker_dependencies: bool,
+ parent_compilation_link_libc: bool,
+ each_lib_rpath: bool,
+ build_id: std.zig.BuildId,
+ disable_lld_caching: bool,
+ hash_style: HashStyle,
+ sort_section: ?SortSection,
+ major_subsystem_version: ?u32,
+ minor_subsystem_version: ?u32,
+ gc_sections: ?bool = null,
+ allow_shlib_undefined: ?bool,
+ subsystem: ?std.Target.SubSystem,
+ version_script: ?[]const u8,
+ soname: ?[]const u8,
+ print_gc_sections: bool,
+ print_icf_sections: bool,
+ print_map: bool,
+ opt_bisect_limit: i32,
+
+ /// List of symbols forced as undefined in the symbol table
+ /// thus forcing their resolution by the linker.
+ /// Corresponds to `-u <symbol>` for ELF/MachO and `/include:<symbol>` for COFF/PE.
+ force_undefined_symbols: std.StringArrayHashMapUnmanaged(void),
+ /// Use a wrapper function for symbol. Any undefined reference to symbol
+ /// will be resolved to __wrap_symbol. Any undefined reference to
+ /// __real_symbol will be resolved to symbol. This can be used to provide a
+ /// wrapper for a system function. The wrapper function should be called
+ /// __wrap_symbol. If it wishes to call the system function, it should call
+ /// __real_symbol.
+ symbol_wrap_set: std.StringArrayHashMapUnmanaged(void),
+
+ compatibility_version: ?std.SemanticVersion,
+
+ dwarf_format: ?std.dwarf.Format,
+
+ // TODO: remove this. libraries are resolved by the frontend.
+ lib_dirs: []const []const u8,
+ rpath_list: []const []const u8,
+
+ /// (Zig compiler development) Enable dumping of linker's state as JSON.
+ enable_link_snapshots: bool,
+
+ /// (Darwin) Install name for the dylib
+ install_name: ?[]const u8,
+ /// (Darwin) Path to entitlements file
+ entitlements: ?[]const u8,
+ /// (Darwin) size of the __PAGEZERO segment
+ pagezero_size: ?u64,
+ /// (Darwin) set minimum space for future expansion of the load commands
+ headerpad_size: ?u32,
+ /// (Darwin) set enough space as if all paths were MATPATHLEN
+ headerpad_max_install_names: bool,
+ /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
+ dead_strip_dylibs: bool,
+ framework_dirs: []const []const u8,
+ frameworks: []const Framework,
+ darwin_sdk_layout: ?MachO.SdkLayout,
+
+ /// (Windows) PDB source path prefix to instruct the linker how to resolve relative
+ /// paths when consolidating CodeView streams into a single PDB file.
+ pdb_source_path: ?[]const u8,
+ /// (Windows) PDB output path
+ pdb_out_path: ?[]const u8,
+ /// (Windows) .def file to specify when linking
+ module_definition_file: ?[]const u8,
+
+ /// (SPIR-V) whether to generate a structured control flow graph or not
+ want_structured_cfg: ?bool,
+
+ wasi_emulated_libs: []const wasi_libc.CRTFile,
+ };
+
/// Attempts incremental linking, if the file already exists. If
/// incremental linking fails, falls back to truncating the file and
/// rewriting it. A malicious file is detected as incremental link failure
/// and does not cause Illegal Behavior. This operation is not atomic.
- pub fn openPath(allocator: Allocator, options: Options) !*File {
- const have_macho = !build_options.only_c;
- if (have_macho and options.target.ofmt == .macho) {
- return &(try MachO.openPath(allocator, options)).base;
- }
-
- if (options.emit == null) {
- return switch (options.target.ofmt) {
- .coff => &(try Coff.createEmpty(allocator, options)).base,
- .elf => &(try Elf.createEmpty(allocator, options)).base,
- .macho => unreachable,
- .wasm => &(try Wasm.createEmpty(allocator, options)).base,
- .plan9 => return &(try Plan9.createEmpty(allocator, options)).base,
- .c => unreachable, // Reported error earlier.
- .spirv => &(try SpirV.createEmpty(allocator, options)).base,
- .nvptx => &(try NvPtx.createEmpty(allocator, options)).base,
- .hex => return error.HexObjectFormatUnimplemented,
- .raw => return error.RawObjectFormatUnimplemented,
- .dxcontainer => return error.DirectXContainerObjectFormatUnimplemented,
- };
- }
- const emit = options.emit.?;
- 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.target.ofmt) {
- .coff => &(try Coff.createEmpty(allocator, options)).base,
- .elf => &(try Elf.createEmpty(allocator, options)).base,
- .macho => unreachable,
- .plan9 => &(try Plan9.createEmpty(allocator, options)).base,
- .wasm => &(try Wasm.createEmpty(allocator, options)).base,
- .c => unreachable, // Reported error earlier.
- .spirv => &(try SpirV.createEmpty(allocator, options)).base,
- .nvptx => &(try NvPtx.createEmpty(allocator, options)).base,
- .hex => return error.HexObjectFormatUnimplemented,
- .raw => return error.RawObjectFormatUnimplemented,
- .dxcontainer => return error.DirectXContainerObjectFormatUnimplemented,
- };
- }
- // Open a temporary object file, not the final output file because we
- // want to link with LLD.
- break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{
- emit.sub_path, options.target.ofmt.fileExt(options.target.cpu.arch),
- });
- } else emit.sub_path;
- errdefer if (use_lld) allocator.free(sub_path);
-
- const file: *File = f: {
- switch (options.target.ofmt) {
- .coff => {
- if (build_options.only_c) unreachable;
- break :f &(try Coff.openPath(allocator, sub_path, options)).base;
- },
- .elf => {
- if (build_options.only_c) unreachable;
- break :f &(try Elf.openPath(allocator, sub_path, options)).base;
- },
- .macho => unreachable,
- .plan9 => {
- if (build_options.only_c) unreachable;
- break :f &(try Plan9.openPath(allocator, sub_path, options)).base;
- },
- .wasm => {
- if (build_options.only_c) unreachable;
- break :f &(try Wasm.openPath(allocator, sub_path, options)).base;
- },
- .c => {
- break :f &(try C.openPath(allocator, sub_path, options)).base;
- },
- .spirv => {
- if (build_options.only_c) unreachable;
- break :f &(try SpirV.openPath(allocator, sub_path, options)).base;
- },
- .nvptx => {
- if (build_options.only_c) unreachable;
- break :f &(try NvPtx.openPath(allocator, sub_path, options)).base;
- },
- .hex => return error.HexObjectFormatUnimplemented,
- .raw => return error.RawObjectFormatUnimplemented,
- .dxcontainer => return error.DirectXContainerObjectFormatUnimplemented,
- }
- };
-
- if (use_lld) {
- // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`,
- // we also want to put the intermediary object file in the cache while the
- // main emit directory is the cwd.
- file.intermediary_basename = sub_path;
+ /// `arena` is used for allocations with the same lifetime as the created File.
+ pub fn open(arena: Allocator, options: OpenOptions) !*File {
+ switch (Tag.fromObjectFormat(options.comp.root_mod.resolved_target.result.ofmt)) {
+ inline else => |tag| {
+ const ptr = try tag.Type().open(arena, options);
+ return &ptr.base;
+ },
}
-
- return file;
}
pub fn cast(base: *File, comptime T: type) ?*T {
@@ -664,56 +474,45 @@ pub const File = struct {
pub fn destroy(base: *File) void {
base.releaseLock();
if (base.file) |f| f.close();
- if (base.intermediary_basename) |sub_path| base.allocator.free(sub_path);
- base.options.system_libs.deinit(base.allocator);
- base.options.force_undefined_symbols.deinit(base.allocator);
switch (base.tag) {
.coff => {
if (build_options.only_c) unreachable;
const parent = @fieldParentPtr(Coff, "base", base);
parent.deinit();
- base.allocator.destroy(parent);
},
.elf => {
if (build_options.only_c) unreachable;
const parent = @fieldParentPtr(Elf, "base", base);
parent.deinit();
- base.allocator.destroy(parent);
},
.macho => {
if (build_options.only_c) unreachable;
const parent = @fieldParentPtr(MachO, "base", base);
parent.deinit();
- base.allocator.destroy(parent);
},
.c => {
const parent = @fieldParentPtr(C, "base", base);
parent.deinit();
- base.allocator.destroy(parent);
},
.wasm => {
if (build_options.only_c) unreachable;
const parent = @fieldParentPtr(Wasm, "base", base);
parent.deinit();
- base.allocator.destroy(parent);
},
.spirv => {
if (build_options.only_c) unreachable;
const parent = @fieldParentPtr(SpirV, "base", base);
parent.deinit();
- base.allocator.destroy(parent);
},
.plan9 => {
if (build_options.only_c) unreachable;
const parent = @fieldParentPtr(Plan9, "base", base);
parent.deinit();
- base.allocator.destroy(parent);
},
.nvptx => {
if (build_options.only_c) unreachable;
const parent = @fieldParentPtr(NvPtx, "base", base);
parent.deinit();
- base.allocator.destroy(parent);
},
}
}
@@ -1197,6 +996,35 @@ pub const File = struct {
spirv,
plan9,
nvptx,
+
+ pub fn Type(comptime tag: Tag) type {
+ return switch (tag) {
+ .coff => Coff,
+ .elf => Elf,
+ .macho => MachO,
+ .c => C,
+ .wasm => Wasm,
+ .spirv => SpirV,
+ .plan9 => Plan9,
+ .nvptx => NvPtx,
+ };
+ }
+
+ pub fn fromObjectFormat(ofmt: std.Target.ObjectFormat) Tag {
+ return switch (ofmt) {
+ .coff => .coff,
+ .elf => .elf,
+ .macho => .macho,
+ .wasm => .wasm,
+ .plan9 => .plan9,
+ .c => .c,
+ .spirv => .spirv,
+ .nvptx => .nvptx,
+ .hex => @panic("TODO implement hex object format"),
+ .raw => @panic("TODO implement raw object format"),
+ .dxcontainer => @panic("TODO implement dxcontainer object format"),
+ };
+ }
};
pub const ErrorFlags = struct {
@@ -1235,6 +1063,33 @@ pub const File = struct {
}
};
+ pub fn effectiveOutputMode(
+ use_lld: bool,
+ output_mode: std.builtin.OutputMode,
+ ) std.builtin.OutputMode {
+ return if (use_lld) .Obj else output_mode;
+ }
+
+ pub fn determineMode(
+ use_lld: bool,
+ output_mode: std.builtin.OutputMode,
+ link_mode: std.builtin.LinkMode,
+ ) fs.File.Mode {
+ // On common systems with a 0o022 umask, 0o777 will still result in a file created
+ // with 0o755 permissions, but it works appropriately if the system is configured
+ // more leniently. As another data point, C's fopen seems to open files with the
+ // 666 mode.
+ const executable_mode = if (builtin.target.os.tag == .windows) 0 else 0o777;
+ switch (effectiveOutputMode(use_lld, output_mode)) {
+ .Lib => return switch (link_mode) {
+ .Dynamic => executable_mode,
+ .Static => fs.File.default_mode,
+ },
+ .Exe => return executable_mode,
+ .Obj => return fs.File.default_mode,
+ }
+ }
+
pub const C = @import("link/C.zig");
pub const Coff = @import("link/Coff.zig");
pub const Plan9 = @import("link/Plan9.zig");
@@ -1245,19 +1100,3 @@ pub const File = struct {
pub const NvPtx = @import("link/NvPtx.zig");
pub const Dwarf = @import("link/Dwarf.zig");
};
-
-pub fn determineMode(options: Options) fs.File.Mode {
- // On common systems with a 0o022 umask, 0o777 will still result in a file created
- // with 0o755 permissions, but it works appropriately if the system is configured
- // more leniently. As another data point, C's fopen seems to open files with the
- // 666 mode.
- const executable_mode = if (builtin.target.os.tag == .windows) 0 else 0o777;
- switch (options.effectiveOutputMode()) {
- .Lib => return switch (options.link_mode) {
- .Dynamic => executable_mode,
- .Static => fs.File.default_mode,
- },
- .Exe => return executable_mode,
- .Obj => return fs.File.default_mode,
- }
-}
src/main.zig
@@ -269,8 +269,6 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
}
}
- defer log_scopes.deinit(gpa);
-
const cmd = args[1];
const cmd_args = args[2..];
if (mem.eql(u8, cmd, "build-exe")) {
@@ -321,7 +319,7 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
} else if (mem.eql(u8, cmd, "init")) {
return cmdInit(gpa, arena, cmd_args);
} else if (mem.eql(u8, cmd, "targets")) {
- const host = try std.zig.system.resolveTargetQuery(.{});
+ const host = resolveTargetQueryOrFatal(.{});
const stdout = io.getStdOut().writer();
return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, host);
} else if (mem.eql(u8, cmd, "version")) {
@@ -404,37 +402,69 @@ const usage_build_generic =
\\ --global-cache-dir [path] Override the global cache directory
\\ --zig-lib-dir [path] Override path to Zig installation lib directory
\\
- \\Compile Options:
+ \\Global Compile Options:
+ \\ --name [name] Compilation unit name (not a file path)
+ \\ --libc [file] Provide a file which specifies libc paths
+ \\ -x language Treat subsequent input files as having type <language>
+ \\ --dep [[import=]name] Add an entry to the next module's import table
+ \\ --mod [name] [src] Create a module based on the current per-module settings.
+ \\ The first module is the main module.
+ \\ "std" can be configured by leaving src blank.
+ \\ After a --mod argument, per-module settings are reset.
+ \\ --error-limit [num] Set the maximum amount of distinct error values
+ \\ -fllvm Force using LLVM as the codegen backend
+ \\ -fno-llvm Prevent using LLVM as the codegen backend
+ \\ -flibllvm Force using the LLVM API in the codegen backend
+ \\ -fno-libllvm Prevent using the LLVM API in the codegen backend
+ \\ -fclang Force using Clang as the C/C++ compilation backend
+ \\ -fno-clang Prevent using Clang as the C/C++ compilation backend
+ \\ -fPIE Force-enable Position Independent Executable
+ \\ -fno-PIE Force-disable Position Independent Executable
+ \\ -flto Force-enable Link Time Optimization (requires LLVM extensions)
+ \\ -fno-lto Force-disable Link Time Optimization
+ \\ -fdll-export-fns Mark exported functions as DLL exports (Windows)
+ \\ -fno-dll-export-fns Force-disable marking exported functions as DLL exports
+ \\ -freference-trace[=num] Show num lines of reference trace per compile error
+ \\ -fno-reference-trace Disable reference trace
+ \\ -fbuiltin Enable implicit builtin knowledge of functions
+ \\ -fno-builtin Disable implicit builtin knowledge of functions
+ \\ -ffunction-sections Places each function in a separate section
+ \\ -fno-function-sections All functions go into same section
+ \\ -fdata-sections Places each data in a separate section
+ \\ -fno-data-sections All data go into same section
+ \\ -fformatted-panics Enable formatted safety panics
+ \\ -fno-formatted-panics Disable formatted safety panics
+ \\ -fstructured-cfg (SPIR-V) force SPIR-V kernels to use structured control flow
+ \\ -fno-structured-cfg (SPIR-V) force SPIR-V kernels to not use structured control flow
+ \\ -mexec-model=[value] (WASI) Execution model
+ \\
+ \\Per-Module Compile Options:
\\ -target [name] <arch><sub>-<os>-<abi> see the targets command
+ \\ -O [mode] Choose what to optimize for
+ \\ Debug (default) Optimizations off, safety on
+ \\ ReleaseFast Optimize for performance, safety off
+ \\ ReleaseSafe Optimize for performance, safety on
+ \\ ReleaseSmall Optimize for small binary, safety off
+ \\ -ofmt=[fmt] Override target object format
+ \\ elf Executable and Linking Format
+ \\ c C source code
+ \\ wasm WebAssembly
+ \\ coff Common Object File Format (Windows)
+ \\ macho macOS relocatables
+ \\ spirv Standard, Portable Intermediate Representation V (SPIR-V)
+ \\ plan9 Plan 9 from Bell Labs object format
+ \\ hex (planned feature) Intel IHEX
+ \\ raw (planned feature) Dump machine code directly
\\ -mcpu [cpu] Specify target CPU and feature set
\\ -mcmodel=[default|tiny| Limit range of code and data virtual addresses
\\ small|kernel|
\\ medium|large]
- \\ -x language Treat subsequent input files as having type <language>
\\ -mred-zone Force-enable the "red-zone"
\\ -mno-red-zone Force-disable the "red-zone"
\\ -fomit-frame-pointer Omit the stack frame pointer
\\ -fno-omit-frame-pointer Store the stack frame pointer
- \\ -mexec-model=[value] (WASI) Execution model
- \\ --name [name] Override root name (not a file path)
- \\ -O [mode] Choose what to optimize for
- \\ Debug (default) Optimizations off, safety on
- \\ ReleaseFast Optimize for performance, safety off
- \\ ReleaseSafe Optimize for performance, safety on
- \\ ReleaseSmall Optimize for small binary, safety off
- \\ --mod [name]:[deps]:[src] Make a module available for dependency under the given name
- \\ deps: [dep],[dep],...
- \\ dep: [[import=]name]
- \\ --deps [dep],[dep],... Set dependency names for the root package
- \\ dep: [[import=]name]
- \\ --main-mod-path Set the directory of the root module
- \\ --error-limit [num] Set the maximum amount of distinct error values
\\ -fPIC Force-enable Position Independent Code
\\ -fno-PIC Force-disable Position Independent Code
- \\ -fPIE Force-enable Position Independent Executable
- \\ -fno-PIE Force-disable Position Independent Executable
- \\ -flto Force-enable Link Time Optimization (requires LLVM extensions)
- \\ -fno-lto Force-disable Link Time Optimization
\\ -fstack-check Enable stack probing in unsafe builds
\\ -fno-stack-check Disable stack probing in safe builds
\\ -fstack-protector Enable stack protection in unsafe builds
@@ -445,47 +475,18 @@ const usage_build_generic =
\\ -fno-valgrind Omit valgrind client requests in debug builds
\\ -fsanitize-thread Enable Thread Sanitizer
\\ -fno-sanitize-thread Disable Thread Sanitizer
- \\ -fdll-export-fns Mark exported functions as DLL exports (Windows)
- \\ -fno-dll-export-fns Force-disable marking exported functions as DLL exports
\\ -funwind-tables Always produce unwind table entries for all functions
\\ -fno-unwind-tables Never produce unwind table entries
- \\ -fllvm Force using LLVM as the codegen backend
- \\ -fno-llvm Prevent using LLVM as the codegen backend
- \\ -flibllvm Force using the LLVM API in the codegen backend
- \\ -fno-libllvm Prevent using the LLVM API in the codegen backend
- \\ -fclang Force using Clang as the C/C++ compilation backend
- \\ -fno-clang Prevent using Clang as the C/C++ compilation backend
- \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
- \\ -fno-reference-trace Disable reference trace
\\ -ferror-tracing Enable error tracing in ReleaseFast mode
\\ -fno-error-tracing Disable error tracing in Debug and ReleaseSafe mode
\\ -fsingle-threaded Code assumes there is only one thread
\\ -fno-single-threaded Code may not assume there is only one thread
- \\ -fbuiltin Enable implicit builtin knowledge of functions
- \\ -fno-builtin Disable implicit builtin knowledge of functions
- \\ -ffunction-sections Places each function in a separate section
- \\ -fno-function-sections All functions go into same section
- \\ -fdata-sections Places each data in a separate section
- \\ -fno-data-sections All data go into same section
\\ -fstrip Omit debug symbols
\\ -fno-strip Keep debug symbols
- \\ -fformatted-panics Enable formatted safety panics
- \\ -fno-formatted-panics Disable formatted safety panics
- \\ -ofmt=[mode] Override target object format
- \\ elf Executable and Linking Format
- \\ c C source code
- \\ wasm WebAssembly
- \\ coff Common Object File Format (Windows)
- \\ macho macOS relocatables
- \\ spirv Standard, Portable Intermediate Representation V (SPIR-V)
- \\ plan9 Plan 9 from Bell Labs object format
- \\ hex (planned feature) Intel IHEX
- \\ raw (planned feature) Dump machine code directly
\\ -idirafter [dir] Add directory to AFTER include search path
\\ -isystem [dir] Add directory to SYSTEM include search path
\\ -I[dir] Add directory to include search path
\\ -D[macro]=[value] Define C [macro] to [value] (1 if [value] omitted)
- \\ --libc [file] Provide a file which specifies libc paths
\\ -cflags [flags] -- Set extra flags for the next positional C source files
\\ -rcflags [flags] -- Set extra flags for the next positional .rc source files
\\ -rcincludes=[type] Set the type of includes to use when compiling .rc source files
@@ -493,26 +494,8 @@ const usage_build_generic =
\\ msvc Use msvc include paths (must be present on the system)
\\ gnu Use mingw include paths (distributed with Zig)
\\ none Do not use any autodetected include paths
- \\ -fstructured-cfg (SPIR-V) force SPIR-V kernels to use structured control flow
- \\ -fno-structured-cfg (SPIR-V) force SPIR-V kernels to not use structured control flow
\\
- \\Link Options:
- \\ -l[lib], --library [lib] Link against system library (only if actually used)
- \\ -needed-l[lib], Link against system library (even if unused)
- \\ --needed-library [lib]
- \\ -weak-l[lib] link against system library marking it and all
- \\ -weak_library [lib] referenced symbols as weak
- \\ -L[d], --library-directory [d] Add a directory to the library search path
- \\ -search_paths_first For each library search path, check for dynamic
- \\ lib then static lib before proceeding to next path.
- \\ -search_paths_first_static For each library search path, check for static
- \\ lib then dynamic lib before proceeding to next path.
- \\ -search_dylibs_first Search for dynamic libs in all library search
- \\ paths, then static libs.
- \\ -search_static_first Search for static libs in all library search
- \\ paths, then dynamic libs.
- \\ -search_dylibs_only Only search for dynamic libs.
- \\ -search_static_only Only search for static libs.
+ \\Global Link Options:
\\ -T[script], --script [script] Use a custom linker script
\\ --version-script [path] Provide a version .map file
\\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
@@ -529,7 +512,6 @@ const usage_build_generic =
\\ -fcompiler-rt Always include compiler-rt symbols in output
\\ -fno-compiler-rt Prevent including compiler-rt symbols in output
\\ -rdynamic Add all symbols to the dynamic symbol table
- \\ -rpath [path] Add directory to the runtime library search path
\\ -feach-lib-rpath Ensure adding rpath for each used dynamic library
\\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library
\\ -fallow-shlib-undefined Allows undefined symbols in shared libraries
@@ -566,11 +548,6 @@ const usage_build_generic =
\\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker
\\ --stack [size] Override default stack size
\\ --image-base [addr] Set base address for executable image
- \\ -framework [name] (Darwin) link against framework
- \\ -needed_framework [name] (Darwin) link against framework (even if unused)
- \\ -needed_library [lib] link against system library (even if unused)
- \\ -weak_framework [name] (Darwin) link against framework and mark it and all referenced symbols as weak
- \\ -F[dir] (Darwin) add search path for frameworks
\\ -install_name=[value] (Darwin) add dylib's install name
\\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature
\\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation
@@ -587,6 +564,30 @@ const usage_build_generic =
\\ --max-memory=[bytes] (WebAssembly) maximum size of the linear memory
\\ --shared-memory (WebAssembly) use shared linear memory
\\ --global-base=[addr] (WebAssembly) where to start to place global data
+ \\
+ \\Per-Module Link Options:
+ \\ -l[lib], --library [lib] Link against system library (only if actually used)
+ \\ -needed-l[lib], Link against system library (even if unused)
+ \\ --needed-library [lib]
+ \\ -weak-l[lib] link against system library marking it and all
+ \\ -weak_library [lib] referenced symbols as weak
+ \\ -L[d], --library-directory [d] Add a directory to the library search path
+ \\ -search_paths_first For each library search path, check for dynamic
+ \\ lib then static lib before proceeding to next path.
+ \\ -search_paths_first_static For each library search path, check for static
+ \\ lib then dynamic lib before proceeding to next path.
+ \\ -search_dylibs_first Search for dynamic libs in all library search
+ \\ paths, then static libs.
+ \\ -search_static_first Search for static libs in all library search
+ \\ paths, then dynamic libs.
+ \\ -search_dylibs_only Only search for dynamic libs.
+ \\ -search_static_only Only search for static libs.
+ \\ -rpath [path] Add directory to the runtime library search path
+ \\ -framework [name] (Darwin) link against framework
+ \\ -needed_framework [name] (Darwin) link against framework (even if unused)
+ \\ -needed_library [lib] link against system library (even if unused)
+ \\ -weak_framework [name] (Darwin) link against framework and mark it and all referenced symbols as weak
+ \\ -F[dir] (Darwin) add search path for frameworks
\\ --export=[value] (WebAssembly) Force a symbol to be exported
\\
\\Test Options:
@@ -758,9 +759,24 @@ const Framework = struct {
};
const CliModule = struct {
- mod: *Package.Module,
- /// still in CLI arg format
- deps_str: []const u8,
+ paths: Package.Module.CreateOptions.Paths,
+ cc_argv: []const []const u8,
+ inherited: Package.Module.CreateOptions.Inherited,
+ target_arch_os_abi: ?[]const u8,
+ target_mcpu: ?[]const u8,
+
+ deps: []const Dep,
+ resolved: ?*Package.Module,
+
+ c_source_files_start: usize,
+ c_source_files_end: usize,
+ rc_source_files_start: usize,
+ rc_source_files_end: usize,
+
+ pub const Dep = struct {
+ key: []const u8,
+ value: []const u8,
+ };
};
fn buildOutputType(
@@ -769,17 +785,12 @@ fn buildOutputType(
all_args: []const []const u8,
arg_mode: ArgMode,
) !void {
- var color: Color = .auto;
- var optimize_mode: std.builtin.OptimizeMode = .Debug;
var provided_name: ?[]const u8 = null;
- var link_mode: ?std.builtin.LinkMode = null;
var dll_export_fns: ?bool = null;
- var single_threaded: ?bool = null;
var root_src_file: ?[]const u8 = null;
var version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 };
var have_version = false;
var compatibility_version: ?std.SemanticVersion = null;
- var strip: ?bool = null;
var formatted_panics: ?bool = null;
var function_sections = false;
var data_sections = false;
@@ -807,30 +818,11 @@ fn buildOutputType(
var emit_docs: Emit = .no;
var emit_implib: Emit = .yes_default_path;
var emit_implib_arg_provided = false;
- var target_arch_os_abi: []const u8 = "native";
+ var target_arch_os_abi: ?[]const u8 = null;
var target_mcpu: ?[]const u8 = null;
- var target_dynamic_linker: ?[]const u8 = null;
- var target_ofmt: ?[]const u8 = null;
- var output_mode: std.builtin.OutputMode = undefined;
var emit_h: Emit = .no;
var soname: SOName = undefined;
- var ensure_libc_on_non_freestanding = false;
- var ensure_libcpp_on_non_freestanding = false;
- var link_libc = false;
- var link_libcpp = false;
- var link_libunwind = false;
var want_native_include_dirs = false;
- var want_pic: ?bool = null;
- var want_pie: ?bool = null;
- var want_lto: ?bool = null;
- var want_unwind_tables: ?bool = null;
- var want_sanitize_c: ?bool = null;
- var want_stack_check: ?bool = null;
- var want_stack_protector: ?u32 = null;
- var want_red_zone: ?bool = null;
- var omit_frame_pointer: ?bool = null;
- var want_valgrind: ?bool = null;
- var want_tsan: ?bool = null;
var want_compiler_rt: ?bool = null;
var rdynamic: bool = false;
var linker_script: ?[]const u8 = null;
@@ -841,15 +833,11 @@ fn buildOutputType(
var linker_compress_debug_sections: ?link.CompressDebugSections = null;
var linker_allow_shlib_undefined: ?bool = null;
var linker_bind_global_refs_locally: ?bool = null;
- var linker_import_memory: ?bool = null;
- var linker_export_memory: ?bool = null;
var linker_import_symbols: bool = false;
var linker_import_table: bool = false;
var linker_export_table: bool = false;
- var linker_force_entry: ?bool = null;
var linker_initial_memory: ?u64 = null;
var linker_max_memory: ?u64 = null;
- var linker_shared_memory: bool = false;
var linker_global_base: ?u64 = null;
var linker_print_gc_sections: bool = false;
var linker_print_icf_sections: bool = false;
@@ -869,23 +857,16 @@ fn buildOutputType(
var linker_dynamicbase = true;
var linker_optimization: ?u8 = null;
var linker_module_definition_file: ?[]const u8 = null;
- var test_evented_io = false;
var test_no_exec = false;
- var entry: ?[]const u8 = null;
var force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .{};
var stack_size_override: ?u64 = null;
var image_base_override: ?u64 = null;
- var use_llvm: ?bool = null;
- var use_lib_llvm: ?bool = null;
- var use_lld: ?bool = null;
- var use_clang: ?bool = null;
var link_eh_frame_hdr = false;
var link_emit_relocs = false;
var each_lib_rpath: ?bool = null;
var build_id: ?std.zig.BuildId = null;
var sysroot: ?[]const u8 = null;
var libc_paths_file: ?[]const u8 = try EnvVar.ZIG_LIBC.get(arena);
- var machine_code_model: std.builtin.CodeModel = .default;
var runtime_args_start: ?usize = null;
var test_filter: ?[]const u8 = null;
var test_name_prefix: ?[]const u8 = null;
@@ -893,12 +874,10 @@ fn buildOutputType(
var override_local_cache_dir: ?[]const u8 = try EnvVar.ZIG_LOCAL_CACHE_DIR.get(arena);
var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
- var main_mod_path: ?[]const u8 = null;
var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;
var subsystem: ?std.Target.SubSystem = null;
var major_subsystem_version: ?u32 = null;
var minor_subsystem_version: ?u32 = null;
- var wasi_exec_model: ?std.builtin.WasiExecModel = null;
var enable_link_snapshots: bool = false;
var debug_incremental: bool = false;
var install_name: ?[]const u8 = null;
@@ -910,63 +889,100 @@ fn buildOutputType(
var headerpad_size: ?u32 = null;
var headerpad_max_install_names: bool = false;
var dead_strip_dylibs: bool = false;
+ var contains_res_file: bool = false;
var reference_trace: ?u32 = null;
- var error_tracing: ?bool = null;
var pdb_out_path: ?[]const u8 = null;
var dwarf_format: ?std.dwarf.Format = null;
var error_limit: ?Module.ErrorInt = null;
var want_structured_cfg: ?bool = null;
- // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
- // This array is populated by zig cc frontend and then has to be converted to zig-style
- // CPU features.
- var llvm_m_args = std.ArrayList([]const u8).init(arena);
- var system_libs = std.StringArrayHashMap(SystemLib).init(arena);
- var wasi_emulated_libs = std.ArrayList(wasi_libc.CRTFile).init(arena);
- var clang_argv = std.ArrayList([]const u8).init(arena);
- var extra_cflags = std.ArrayList([]const u8).init(arena);
- var extra_rcflags = std.ArrayList([]const u8).init(arena);
// These are before resolving sysroot.
- var lib_dir_args = std.ArrayList([]const u8).init(arena);
- var rpath_list = std.ArrayList([]const u8).init(arena);
+ var lib_dir_args: std.ArrayListUnmanaged([]const u8) = .{};
+ var extra_cflags: std.ArrayListUnmanaged([]const u8) = .{};
+ var extra_rcflags: std.ArrayListUnmanaged([]const u8) = .{};
var symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{};
- var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena);
- var rc_source_files = std.ArrayList(Compilation.RcSourceFile).init(arena);
+ var rpath_list: std.ArrayListUnmanaged([]const u8) = .{};
var rc_includes: Compilation.RcIncludes = .any;
- var res_files = std.ArrayList(Compilation.LinkObject).init(arena);
var manifest_file: ?[]const u8 = null;
- var link_objects = std.ArrayList(Compilation.LinkObject).init(arena);
- var framework_dirs = std.ArrayList([]const u8).init(arena);
+ var link_objects: std.ArrayListUnmanaged(Compilation.LinkObject) = .{};
+ var framework_dirs: std.ArrayListUnmanaged([]const u8) = .{};
var frameworks: std.StringArrayHashMapUnmanaged(Framework) = .{};
+ var linker_export_symbol_names: std.ArrayListUnmanaged([]const u8) = .{};
+
+ // Tracks the position in c_source_files which have already their owner populated.
+ var c_source_files_owner_index: usize = 0;
+ // Tracks the position in rc_source_files which have already their owner populated.
+ var rc_source_files_owner_index: usize = 0;
+
// null means replace with the test executable binary
var test_exec_args = std.ArrayList(?[]const u8).init(arena);
- var linker_export_symbol_names = std.ArrayList([]const u8).init(arena);
+
+ // These get set by CLI flags and then snapshotted when a `--mod` flag is
+ // encountered.
+ var mod_opts: Package.Module.CreateOptions.Inherited = .{};
+
+ // These get appended to by CLI flags and then slurped when a `--mod` flag
+ // is encountered.
+ var cssan: ClangSearchSanitizer = .{};
+ var clang_argv: std.ArrayListUnmanaged([]const u8) = .{};
+ var deps: std.ArrayListUnmanaged(CliModule.Dep) = .{};
+
// Contains every module specified via --mod. The dependencies are added
// after argument parsing is completed. We use a StringArrayHashMap to make
- // error output consistent.
- var modules = std.StringArrayHashMap(CliModule).init(arena);
+ // error output consistent. "root" is special.
+ var create_module: CreateModule = .{
+ // Populated just before the call to `createModule`.
+ .global_cache_directory = undefined,
+ .object_format = null,
+ .dynamic_linker = null,
+ .modules = .{},
+ .opts = .{
+ .is_test = arg_mode == .zig_test,
+ // Populated while parsing CLI args.
+ .output_mode = undefined,
+ // Populated in the call to `createModule` for the root module.
+ .resolved_target = undefined,
+ .have_zcu = false,
+ // Populated just before the call to `createModule`.
+ .emit_llvm_ir = undefined,
+ // Populated just before the call to `createModule`.
+ .emit_llvm_bc = undefined,
+ // Populated just before the call to `createModule`.
+ .emit_bin = undefined,
+ // Populated just before the call to `createModule`.
+ .c_source_files_len = undefined,
+ },
+ // Populated in the call to `createModule` for the root module.
+ .resolved_options = undefined,
- // The dependency string for the root package
- var root_deps_str: ?[]const u8 = null;
+ .system_libs = .{},
+ .external_system_libs = .{},
+ .resolved_system_libs = .{},
+ .wasi_emulated_libs = .{},
+
+ .c_source_files = .{},
+ .rc_source_files = .{},
+
+ .llvm_m_args = .{},
+ };
// before arg parsing, check for the NO_COLOR environment variable
// if it exists, default the color setting to .off
// explicit --color arguments will still override this setting.
// Disable color on WASI per https://github.com/WebAssembly/WASI/issues/162
- color = if (builtin.os.tag == .wasi or EnvVar.NO_COLOR.isSet()) .off else .auto;
+ var color: Color = if (builtin.os.tag == .wasi or EnvVar.NO_COLOR.isSet()) .off else .auto;
switch (arg_mode) {
.build, .translate_c, .zig_test, .run => {
- var optimize_mode_string: ?[]const u8 = null;
switch (arg_mode) {
.build => |m| {
- output_mode = m;
+ create_module.opts.output_mode = m;
},
.translate_c => {
emit_bin = .no;
- output_mode = .Obj;
+ create_module.opts.output_mode = .Obj;
},
.zig_test, .run => {
- output_mode = .Exe;
+ create_module.opts.output_mode = .Exe;
},
else => unreachable,
}
@@ -977,9 +993,6 @@ fn buildOutputType(
.args = all_args[2..],
};
- var cssan = ClangSearchSanitizer.init(gpa, &clang_argv);
- defer cssan.map.deinit();
-
var file_ext: ?Compilation.FileExt = null;
args_loop: while (args_iter.next()) |arg| {
if (mem.startsWith(u8, arg, "@")) {
@@ -1002,49 +1015,73 @@ fn buildOutputType(
} else {
fatal("unexpected end-of-parameter mark: --", .{});
}
- } else if (mem.eql(u8, arg, "--mod")) {
- const info = args_iter.nextOrFatal();
- var info_it = mem.splitScalar(u8, info, ':');
- const mod_name = info_it.next() orelse fatal("expected non-empty argument after {s}", .{arg});
- const deps_str = info_it.next() orelse fatal("expected 'name:deps:path' after {s}", .{arg});
- const root_src_orig = info_it.rest();
- if (root_src_orig.len == 0) fatal("expected 'name:deps:path' after {s}", .{arg});
- if (mod_name.len == 0) fatal("empty name for module at '{s}'", .{root_src_orig});
-
- const root_src = try introspect.resolvePath(arena, root_src_orig);
-
- for ([_][]const u8{ "std", "root", "builtin" }) |name| {
- if (mem.eql(u8, mod_name, name)) {
- fatal("unable to add module '{s}' -> '{s}': conflicts with builtin module", .{
- mod_name, root_src,
+ } else if (mem.eql(u8, arg, "--dep")) {
+ var it = mem.splitScalar(u8, args_iter.nextOrFatal(), '=');
+ const key = it.next().?;
+ const value = it.next() orelse key;
+ if (mem.eql(u8, key, "std") and !mem.eql(u8, value, "std")) {
+ fatal("unable to import as '{s}': conflicts with builtin module", .{
+ key,
+ });
+ }
+ for ([_][]const u8{ "root", "builtin" }) |name| {
+ if (mem.eql(u8, key, name)) {
+ fatal("unable to import as '{s}': conflicts with builtin module", .{
+ key,
});
}
}
+ try deps.append(arena, .{
+ .key = key,
+ .value = value,
+ });
+ } else if (mem.eql(u8, arg, "--mod")) {
+ const mod_name = args_iter.nextOrFatal();
+ const root_src_orig = args_iter.nextOrFatal();
- if (modules.get(mod_name)) |value| {
- fatal("unable to add module '{s}' -> '{s}': already exists as '{s}'", .{
- mod_name, root_src, value.mod.root_src_path,
+ const gop = try create_module.modules.getOrPut(arena, mod_name);
+
+ if (gop.found_existing) {
+ fatal("unable to add module '{s}': already exists as '{s}'", .{
+ mod_name, gop.value_ptr.paths.root_src_path,
});
}
- try modules.put(mod_name, .{
- .mod = try Package.Module.create(arena, .{
+ // See duplicate logic: ModCreationGlobalFlags
+ create_module.opts.have_zcu = true;
+ if (mod_opts.single_threaded == false)
+ create_module.opts.any_non_single_threaded = true;
+ if (mod_opts.sanitize_thread == true)
+ create_module.opts.any_sanitize_thread = true;
+ if (mod_opts.unwind_tables == true)
+ create_module.opts.any_unwind_tables = true;
+
+ const root_src = try introspect.resolvePath(arena, root_src_orig);
+ try create_module.modules.put(arena, mod_name, .{
+ .paths = .{
.root = .{
.root_dir = Cache.Directory.cwd(),
.sub_path = fs.path.dirname(root_src) orelse "",
},
.root_src_path = fs.path.basename(root_src),
- .fully_qualified_name = mod_name,
- }),
- .deps_str = deps_str,
+ },
+ .cc_argv = try clang_argv.toOwnedSlice(arena),
+ .inherited = mod_opts,
+ .target_arch_os_abi = target_arch_os_abi,
+ .target_mcpu = target_mcpu,
+ .deps = try deps.toOwnedSlice(arena),
+ .resolved = null,
+ .c_source_files_start = c_source_files_owner_index,
+ .c_source_files_end = create_module.c_source_files.items.len,
+ .rc_source_files_start = rc_source_files_owner_index,
+ .rc_source_files_end = create_module.rc_source_files.items.len,
});
- } else if (mem.eql(u8, arg, "--deps")) {
- if (root_deps_str != null) {
- fatal("only one --deps argument is allowed", .{});
- }
- root_deps_str = args_iter.nextOrFatal();
- } else if (mem.eql(u8, arg, "--main-mod-path")) {
- main_mod_path = args_iter.nextOrFatal();
+ cssan.reset();
+ mod_opts = .{};
+ target_arch_os_abi = null;
+ target_mcpu = null;
+ c_source_files_owner_index = create_module.c_source_files.items.len;
+ rc_source_files_owner_index = create_module.rc_source_files.items.len;
} else if (mem.eql(u8, arg, "--error-limit")) {
const next_arg = args_iter.nextOrFatal();
error_limit = std.fmt.parseUnsigned(Module.ErrorInt, next_arg, 0) catch |err| {
@@ -1057,7 +1094,7 @@ fn buildOutputType(
fatal("expected -- after -cflags", .{});
};
if (mem.eql(u8, next_arg, "--")) break;
- try extra_cflags.append(next_arg);
+ try extra_cflags.append(arena, next_arg);
}
} else if (mem.eql(u8, arg, "-rcincludes")) {
rc_includes = parseRcIncludes(args_iter.nextOrFatal());
@@ -1070,7 +1107,7 @@ fn buildOutputType(
fatal("expected -- after -rcflags", .{});
};
if (mem.eql(u8, next_arg, "--")) break;
- try extra_rcflags.append(next_arg);
+ try extra_rcflags.append(arena, next_arg);
}
} else if (mem.startsWith(u8, arg, "-fstructured-cfg")) {
want_structured_cfg = true;
@@ -1086,11 +1123,11 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "--subsystem")) {
subsystem = try parseSubSystem(args_iter.nextOrFatal());
} else if (mem.eql(u8, arg, "-O")) {
- optimize_mode_string = args_iter.nextOrFatal();
+ mod_opts.optimize_mode = parseOptimizeMode(args_iter.nextOrFatal());
} else if (mem.startsWith(u8, arg, "-fentry=")) {
- entry = arg["-fentry=".len..];
+ create_module.opts.entry = .{ .named = arg["-fentry=".len..] };
} else if (mem.eql(u8, arg, "--force_undefined")) {
- try force_undefined_symbols.put(gpa, args_iter.nextOrFatal(), {});
+ try force_undefined_symbols.put(arena, args_iter.nextOrFatal(), {});
} else if (mem.eql(u8, arg, "--stack")) {
const next_arg = args_iter.nextOrFatal();
stack_size_override = std.fmt.parseUnsigned(u64, next_arg, 0) catch |err| {
@@ -1106,17 +1143,17 @@ fn buildOutputType(
if (!mem.eql(u8, provided_name.?, fs.path.basename(provided_name.?)))
fatal("invalid package name '{s}': cannot contain folder separators", .{provided_name.?});
} else if (mem.eql(u8, arg, "-rpath")) {
- try rpath_list.append(args_iter.nextOrFatal());
+ try rpath_list.append(arena, args_iter.nextOrFatal());
} else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
- try lib_dir_args.append(args_iter.nextOrFatal());
+ try lib_dir_args.append(arena, args_iter.nextOrFatal());
} else if (mem.eql(u8, arg, "-F")) {
- try framework_dirs.append(args_iter.nextOrFatal());
+ try framework_dirs.append(arena, args_iter.nextOrFatal());
} else if (mem.eql(u8, arg, "-framework")) {
- try frameworks.put(gpa, args_iter.nextOrFatal(), .{});
+ try frameworks.put(arena, args_iter.nextOrFatal(), .{});
} else if (mem.eql(u8, arg, "-weak_framework")) {
- try frameworks.put(gpa, args_iter.nextOrFatal(), .{ .weak = true });
+ try frameworks.put(arena, args_iter.nextOrFatal(), .{ .weak = true });
} else if (mem.eql(u8, arg, "-needed_framework")) {
- try frameworks.put(gpa, args_iter.nextOrFatal(), .{ .needed = true });
+ try frameworks.put(arena, args_iter.nextOrFatal(), .{ .needed = true });
} else if (mem.eql(u8, arg, "-install_name")) {
install_name = args_iter.nextOrFatal();
} else if (mem.startsWith(u8, arg, "--compress-debug-sections=")) {
@@ -1168,7 +1205,7 @@ fn buildOutputType(
// We don't know whether this library is part of libc
// or libc++ until we resolve the target, so we append
// to the list for now.
- try system_libs.put(args_iter.nextOrFatal(), .{
+ try create_module.system_libs.put(arena, args_iter.nextOrFatal(), .{
.needed = false,
.weak = false,
.preferred_mode = lib_preferred_mode,
@@ -1179,38 +1216,37 @@ fn buildOutputType(
mem.eql(u8, arg, "-needed_library"))
{
const next_arg = args_iter.nextOrFatal();
- try system_libs.put(next_arg, .{
+ try create_module.system_libs.put(arena, next_arg, .{
.needed = true,
.weak = false,
.preferred_mode = lib_preferred_mode,
.search_strategy = lib_search_strategy,
});
} else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {
- try system_libs.put(args_iter.nextOrFatal(), .{
+ try create_module.system_libs.put(arena, args_iter.nextOrFatal(), .{
.needed = false,
.weak = true,
.preferred_mode = lib_preferred_mode,
.search_strategy = lib_search_strategy,
});
} else if (mem.eql(u8, arg, "-D")) {
- try clang_argv.append(arg);
- try clang_argv.append(args_iter.nextOrFatal());
+ try clang_argv.appendSlice(arena, &.{ arg, args_iter.nextOrFatal() });
} else if (mem.eql(u8, arg, "-I")) {
- try cssan.addIncludePath(.I, arg, args_iter.nextOrFatal(), false);
+ try cssan.addIncludePath(arena, &clang_argv, .I, arg, args_iter.nextOrFatal(), false);
} else if (mem.eql(u8, arg, "-isystem")) {
- try cssan.addIncludePath(.isystem, arg, args_iter.nextOrFatal(), false);
+ try cssan.addIncludePath(arena, &clang_argv, .isystem, arg, args_iter.nextOrFatal(), false);
} else if (mem.eql(u8, arg, "-iwithsysroot")) {
- try cssan.addIncludePath(.iwithsysroot, arg, args_iter.nextOrFatal(), false);
+ try cssan.addIncludePath(arena, &clang_argv, .iwithsysroot, arg, args_iter.nextOrFatal(), false);
} else if (mem.eql(u8, arg, "-idirafter")) {
- try cssan.addIncludePath(.idirafter, arg, args_iter.nextOrFatal(), false);
+ try cssan.addIncludePath(arena, &clang_argv, .idirafter, arg, args_iter.nextOrFatal(), false);
} else if (mem.eql(u8, arg, "-iframework")) {
const path = args_iter.nextOrFatal();
- try cssan.addIncludePath(.iframework, arg, path, false);
- try framework_dirs.append(path); // Forward to the backend as -F
+ try cssan.addIncludePath(arena, &clang_argv, .iframework, arg, path, false);
+ try framework_dirs.append(arena, path); // Forward to the backend as -F
} else if (mem.eql(u8, arg, "-iframeworkwithsysroot")) {
const path = args_iter.nextOrFatal();
- try cssan.addIncludePath(.iframeworkwithsysroot, arg, path, false);
- try framework_dirs.append(path); // Forward to the backend as -F
+ try cssan.addIncludePath(arena, &clang_argv, .iframeworkwithsysroot, arg, path, false);
+ try framework_dirs.append(arena, path); // Forward to the backend as -F
} else if (mem.eql(u8, arg, "--version")) {
const next_arg = args_iter.nextOrFatal();
version = std.SemanticVersion.parse(next_arg) catch |err| {
@@ -1222,21 +1258,21 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "-mcpu")) {
target_mcpu = args_iter.nextOrFatal();
} else if (mem.eql(u8, arg, "-mcmodel")) {
- machine_code_model = parseCodeModel(args_iter.nextOrFatal());
+ mod_opts.code_model = parseCodeModel(args_iter.nextOrFatal());
+ } else if (mem.startsWith(u8, arg, "-mcmodel=")) {
+ mod_opts.code_model = parseCodeModel(arg["-mcmodel=".len..]);
} else if (mem.startsWith(u8, arg, "-ofmt=")) {
- target_ofmt = arg["-ofmt=".len..];
+ create_module.object_format = arg["-ofmt=".len..];
} else if (mem.startsWith(u8, arg, "-mcpu=")) {
target_mcpu = arg["-mcpu=".len..];
- } else if (mem.startsWith(u8, arg, "-mcmodel=")) {
- machine_code_model = parseCodeModel(arg["-mcmodel=".len..]);
} else if (mem.startsWith(u8, arg, "-O")) {
- optimize_mode_string = arg["-O".len..];
+ mod_opts.optimize_mode = parseOptimizeMode(arg["-O".len..]);
} else if (mem.eql(u8, arg, "--dynamic-linker")) {
- target_dynamic_linker = args_iter.nextOrFatal();
+ create_module.dynamic_linker = args_iter.nextOrFatal();
} else if (mem.eql(u8, arg, "--sysroot")) {
- sysroot = args_iter.nextOrFatal();
- try clang_argv.append("-isysroot");
- try clang_argv.append(sysroot.?);
+ const next_arg = args_iter.nextOrFatal();
+ sysroot = next_arg;
+ try clang_argv.appendSlice(arena, &.{ "-isysroot", next_arg });
} else if (mem.eql(u8, arg, "--libc")) {
libc_paths_file = args_iter.nextOrFatal();
} else if (mem.eql(u8, arg, "--test-filter")) {
@@ -1258,7 +1294,7 @@ fn buildOutputType(
warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{});
_ = args_iter.nextOrFatal();
} else {
- try log_scopes.append(gpa, args_iter.nextOrFatal());
+ try log_scopes.append(arena, args_iter.nextOrFatal());
}
} else if (mem.eql(u8, arg, "--listen")) {
const next_arg = args_iter.nextOrFatal();
@@ -1298,7 +1334,7 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "--test-cmd-bin")) {
try test_exec_args.append(null);
} else if (mem.eql(u8, arg, "--test-evented-io")) {
- test_evented_io = true;
+ create_module.opts.test_evented_io = true;
} else if (mem.eql(u8, arg, "--test-no-exec")) {
test_no_exec = true;
} else if (mem.eql(u8, arg, "-ftime-report")) {
@@ -1306,65 +1342,65 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "-fstack-report")) {
stack_report = true;
} else if (mem.eql(u8, arg, "-fPIC")) {
- want_pic = true;
+ mod_opts.pic = true;
} else if (mem.eql(u8, arg, "-fno-PIC")) {
- want_pic = false;
+ mod_opts.pic = false;
} else if (mem.eql(u8, arg, "-fPIE")) {
- want_pie = true;
+ create_module.opts.pie = true;
} else if (mem.eql(u8, arg, "-fno-PIE")) {
- want_pie = false;
+ create_module.opts.pie = false;
} else if (mem.eql(u8, arg, "-flto")) {
- want_lto = true;
+ create_module.opts.lto = true;
} else if (mem.eql(u8, arg, "-fno-lto")) {
- want_lto = false;
+ create_module.opts.lto = false;
} else if (mem.eql(u8, arg, "-funwind-tables")) {
- want_unwind_tables = true;
+ mod_opts.unwind_tables = true;
} else if (mem.eql(u8, arg, "-fno-unwind-tables")) {
- want_unwind_tables = false;
+ mod_opts.unwind_tables = false;
} else if (mem.eql(u8, arg, "-fstack-check")) {
- want_stack_check = true;
+ mod_opts.stack_check = true;
} else if (mem.eql(u8, arg, "-fno-stack-check")) {
- want_stack_check = false;
+ mod_opts.stack_check = false;
} else if (mem.eql(u8, arg, "-fstack-protector")) {
- want_stack_protector = Compilation.default_stack_protector_buffer_size;
+ mod_opts.stack_protector = Compilation.default_stack_protector_buffer_size;
} else if (mem.eql(u8, arg, "-fno-stack-protector")) {
- want_stack_protector = 0;
+ mod_opts.stack_protector = 0;
} else if (mem.eql(u8, arg, "-mred-zone")) {
- want_red_zone = true;
+ mod_opts.red_zone = true;
} else if (mem.eql(u8, arg, "-mno-red-zone")) {
- want_red_zone = false;
+ mod_opts.red_zone = false;
} else if (mem.eql(u8, arg, "-fomit-frame-pointer")) {
- omit_frame_pointer = true;
+ mod_opts.omit_frame_pointer = true;
} else if (mem.eql(u8, arg, "-fno-omit-frame-pointer")) {
- omit_frame_pointer = false;
+ mod_opts.omit_frame_pointer = false;
} else if (mem.eql(u8, arg, "-fsanitize-c")) {
- want_sanitize_c = true;
+ mod_opts.sanitize_c = true;
} else if (mem.eql(u8, arg, "-fno-sanitize-c")) {
- want_sanitize_c = false;
+ mod_opts.sanitize_c = false;
} else if (mem.eql(u8, arg, "-fvalgrind")) {
- want_valgrind = true;
+ mod_opts.valgrind = true;
} else if (mem.eql(u8, arg, "-fno-valgrind")) {
- want_valgrind = false;
+ mod_opts.valgrind = false;
} else if (mem.eql(u8, arg, "-fsanitize-thread")) {
- want_tsan = true;
+ mod_opts.sanitize_thread = true;
} else if (mem.eql(u8, arg, "-fno-sanitize-thread")) {
- want_tsan = false;
+ mod_opts.sanitize_thread = false;
} else if (mem.eql(u8, arg, "-fllvm")) {
- use_llvm = true;
+ create_module.opts.use_llvm = true;
} else if (mem.eql(u8, arg, "-fno-llvm")) {
- use_llvm = false;
+ create_module.opts.use_llvm = false;
} else if (mem.eql(u8, arg, "-flibllvm")) {
- use_lib_llvm = true;
+ create_module.opts.use_lib_llvm = true;
} else if (mem.eql(u8, arg, "-fno-libllvm")) {
- use_lib_llvm = false;
+ create_module.opts.use_lib_llvm = false;
} else if (mem.eql(u8, arg, "-flld")) {
- use_lld = true;
+ create_module.opts.use_lld = true;
} else if (mem.eql(u8, arg, "-fno-lld")) {
- use_lld = false;
+ create_module.opts.use_lld = false;
} else if (mem.eql(u8, arg, "-fclang")) {
- use_clang = true;
+ create_module.opts.use_clang = true;
} else if (mem.eql(u8, arg, "-fno-clang")) {
- use_clang = false;
+ create_module.opts.use_clang = false;
} else if (mem.eql(u8, arg, "-freference-trace")) {
reference_trace = 256;
} else if (mem.startsWith(u8, arg, "-freference-trace=")) {
@@ -1375,9 +1411,9 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "-fno-reference-trace")) {
reference_trace = null;
} else if (mem.eql(u8, arg, "-ferror-tracing")) {
- error_tracing = true;
+ mod_opts.error_tracing = true;
} else if (mem.eql(u8, arg, "-fno-error-tracing")) {
- error_tracing = false;
+ mod_opts.error_tracing = false;
} else if (mem.eql(u8, arg, "-rdynamic")) {
rdynamic = true;
} else if (mem.eql(u8, arg, "-fsoname")) {
@@ -1432,11 +1468,11 @@ fn buildOutputType(
emit_implib = .no;
emit_implib_arg_provided = true;
} else if (mem.eql(u8, arg, "-dynamic")) {
- link_mode = .Dynamic;
+ create_module.opts.link_mode = .Dynamic;
lib_preferred_mode = .Dynamic;
lib_search_strategy = .mode_first;
} else if (mem.eql(u8, arg, "-static")) {
- link_mode = .Static;
+ create_module.opts.link_mode = .Static;
lib_preferred_mode = .Static;
lib_search_strategy = .no_fallback;
} else if (mem.eql(u8, arg, "-fdll-export-fns")) {
@@ -1447,9 +1483,9 @@ fn buildOutputType(
show_builtin = true;
emit_bin = .no;
} else if (mem.eql(u8, arg, "-fstrip")) {
- strip = true;
+ mod_opts.strip = true;
} else if (mem.eql(u8, arg, "-fno-strip")) {
- strip = false;
+ mod_opts.strip = false;
} else if (mem.eql(u8, arg, "-gdwarf32")) {
dwarf_format = .@"32";
} else if (mem.eql(u8, arg, "-gdwarf64")) {
@@ -1459,9 +1495,9 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "-fno-formatted-panics")) {
formatted_panics = false;
} else if (mem.eql(u8, arg, "-fsingle-threaded")) {
- single_threaded = true;
+ mod_opts.single_threaded = true;
} else if (mem.eql(u8, arg, "-fno-single-threaded")) {
- single_threaded = false;
+ mod_opts.single_threaded = false;
} else if (mem.eql(u8, arg, "-ffunction-sections")) {
function_sections = true;
} else if (mem.eql(u8, arg, "-fno-function-sections")) {
@@ -1518,13 +1554,16 @@ fn buildOutputType(
fatal("unsupported linker extension flag: -z {s}", .{z_arg});
}
} else if (mem.eql(u8, arg, "--import-memory")) {
- linker_import_memory = true;
+ create_module.opts.import_memory = true;
} else if (mem.eql(u8, arg, "-fentry")) {
- linker_force_entry = true;
+ switch (create_module.opts.entry) {
+ .default, .disabled => create_module.opts.entry = .enabled,
+ .enabled, .named => {},
+ }
} else if (mem.eql(u8, arg, "-fno-entry")) {
- linker_force_entry = false;
+ create_module.opts.entry = .disabled;
} else if (mem.eql(u8, arg, "--export-memory")) {
- linker_export_memory = true;
+ create_module.opts.export_memory = true;
} else if (mem.eql(u8, arg, "--import-symbols")) {
linker_import_symbols = true;
} else if (mem.eql(u8, arg, "--import-table")) {
@@ -1536,11 +1575,11 @@ fn buildOutputType(
} else if (mem.startsWith(u8, arg, "--max-memory=")) {
linker_max_memory = parseIntSuffix(arg, "--max-memory=".len);
} else if (mem.eql(u8, arg, "--shared-memory")) {
- linker_shared_memory = true;
+ create_module.opts.shared_memory = true;
} else if (mem.startsWith(u8, arg, "--global-base=")) {
linker_global_base = parseIntSuffix(arg, "--global-base=".len);
} else if (mem.startsWith(u8, arg, "--export=")) {
- try linker_export_symbol_names.append(arg["--export=".len..]);
+ try linker_export_symbol_names.append(arena, arg["--export=".len..]);
} else if (mem.eql(u8, arg, "-Bsymbolic")) {
linker_bind_global_refs_locally = true;
} else if (mem.eql(u8, arg, "--gc-sections")) {
@@ -1585,37 +1624,37 @@ fn buildOutputType(
} else if (mem.startsWith(u8, arg, "-T")) {
linker_script = arg[2..];
} else if (mem.startsWith(u8, arg, "-L")) {
- try lib_dir_args.append(arg[2..]);
+ try lib_dir_args.append(arena, arg[2..]);
} else if (mem.startsWith(u8, arg, "-F")) {
- try framework_dirs.append(arg[2..]);
+ try framework_dirs.append(arena, arg[2..]);
} else if (mem.startsWith(u8, arg, "-l")) {
// We don't know whether this library is part of libc
// or libc++ until we resolve the target, so we append
// to the list for now.
- try system_libs.put(arg["-l".len..], .{
+ try create_module.system_libs.put(arena, arg["-l".len..], .{
.needed = false,
.weak = false,
.preferred_mode = lib_preferred_mode,
.search_strategy = lib_search_strategy,
});
} else if (mem.startsWith(u8, arg, "-needed-l")) {
- try system_libs.put(arg["-needed-l".len..], .{
+ try create_module.system_libs.put(arena, arg["-needed-l".len..], .{
.needed = true,
.weak = false,
.preferred_mode = lib_preferred_mode,
.search_strategy = lib_search_strategy,
});
} else if (mem.startsWith(u8, arg, "-weak-l")) {
- try system_libs.put(arg["-weak-l".len..], .{
+ try create_module.system_libs.put(arena, arg["-weak-l".len..], .{
.needed = false,
.weak = true,
.preferred_mode = lib_preferred_mode,
.search_strategy = lib_search_strategy,
});
} else if (mem.startsWith(u8, arg, "-D")) {
- try clang_argv.append(arg);
+ try clang_argv.append(arena, arg);
} else if (mem.startsWith(u8, arg, "-I")) {
- try cssan.addIncludePath(.I, arg, arg[2..], true);
+ try cssan.addIncludePath(arena, &clang_argv, .I, arg, arg[2..], true);
} else if (mem.eql(u8, arg, "-x")) {
const lang = args_iter.nextOrFatal();
if (mem.eql(u8, lang, "none")) {
@@ -1626,23 +1665,27 @@ fn buildOutputType(
fatal("language not recognized: '{s}'", .{lang});
}
} else if (mem.startsWith(u8, arg, "-mexec-model=")) {
- wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, arg["-mexec-model=".len..]) orelse {
- fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{arg["-mexec-model=".len..]});
- };
+ create_module.opts.wasi_exec_model = parseWasiExecModel(arg["-mexec-model=".len..]);
} else {
fatal("unrecognized parameter: '{s}'", .{arg});
}
- } else switch (file_ext orelse
- Compilation.classifyFileExt(arg)) {
- .object, .static_library, .shared_library => try link_objects.append(.{ .path = arg }),
- .res => try res_files.append(.{ .path = arg }),
+ } else switch (file_ext orelse Compilation.classifyFileExt(arg)) {
+ .object, .static_library, .shared_library => {
+ try link_objects.append(arena, .{ .path = arg });
+ },
+ .res => {
+ try link_objects.append(arena, .{ .path = arg });
+ contains_res_file = true;
+ },
.manifest => {
if (manifest_file) |other| {
fatal("only one manifest file can be specified, found '{s}' after '{s}'", .{ arg, other });
} else manifest_file = arg;
},
.assembly, .assembly_with_cpp, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => {
- try c_source_files.append(.{
+ try create_module.c_source_files.append(arena, .{
+ // Populated after module creation.
+ .owner = undefined,
.src_path = arg,
.extra_flags = try arena.dupe([]const u8, extra_cflags.items),
// duped when parsing the args.
@@ -1650,7 +1693,9 @@ fn buildOutputType(
});
},
.rc => {
- try rc_source_files.append(.{
+ try create_module.rc_source_files.append(arena, .{
+ // Populated after module creation.
+ .owner = undefined,
.src_path = arg,
.extra_flags = try arena.dupe([]const u8, extra_rcflags.items),
});
@@ -1668,18 +1713,14 @@ fn buildOutputType(
},
}
}
- if (optimize_mode_string) |s| {
- optimize_mode = std.meta.stringToEnum(std.builtin.OptimizeMode, s) orelse
- fatal("unrecognized optimization mode: '{s}'", .{s});
- }
},
.cc, .cpp => {
if (build_options.only_c) unreachable;
emit_h = .no;
soname = .no;
- ensure_libc_on_non_freestanding = true;
- ensure_libcpp_on_non_freestanding = arg_mode == .cpp;
+ create_module.opts.ensure_libc_on_non_freestanding = true;
+ create_module.opts.ensure_libcpp_on_non_freestanding = arg_mode == .cpp;
want_native_include_dirs = true;
// Clang's driver enables this switch unconditionally.
// Disabling the emission of .eh_frame_hdr can unexpectedly break
@@ -1733,24 +1774,30 @@ fn buildOutputType(
}
},
.other => {
- try clang_argv.appendSlice(it.other_args);
+ try clang_argv.appendSlice(arena, it.other_args);
},
- .positional => switch (file_ext orelse
- Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0))) {
+ .positional => switch (file_ext orelse Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0))) {
.assembly, .assembly_with_cpp, .c, .cpp, .ll, .bc, .h, .m, .mm, .cu => {
- try c_source_files.append(.{
+ try create_module.c_source_files.append(arena, .{
+ // Populated after module creation.
+ .owner = undefined,
.src_path = it.only_arg,
.ext = file_ext, // duped while parsing the args.
});
},
- .unknown, .shared_library, .object, .static_library => try link_objects.append(.{
- .path = it.only_arg,
- .must_link = must_link,
- }),
- .res => try res_files.append(.{
- .path = it.only_arg,
- .must_link = must_link,
- }),
+ .unknown, .shared_library, .object, .static_library => {
+ try link_objects.append(arena, .{
+ .path = it.only_arg,
+ .must_link = must_link,
+ });
+ },
+ .res => {
+ try link_objects.append(arena, .{
+ .path = it.only_arg,
+ .must_link = must_link,
+ });
+ contains_res_file = true;
+ },
.manifest => {
if (manifest_file) |other| {
fatal("only one manifest file can be specified, found '{s}' after previously specified manifest '{s}'", .{ it.only_arg, other });
@@ -1760,7 +1807,11 @@ fn buildOutputType(
linker_module_definition_file = it.only_arg;
},
.rc => {
- try rc_source_files.append(.{ .src_path = it.only_arg });
+ try create_module.rc_source_files.append(arena, .{
+ // Populated after module creation.
+ .owner = undefined,
+ .src_path = it.only_arg,
+ });
},
.zig => {
if (root_src_file) |other| {
@@ -1777,13 +1828,13 @@ fn buildOutputType(
// more control over what's in the resulting
// binary: no extra rpaths and DSO filename exactly
// as provided. Hello, Go.
- try link_objects.append(.{
+ try link_objects.append(arena, .{
.path = it.only_arg,
.must_link = must_link,
.loption = true,
});
} else {
- try system_libs.put(it.only_arg, .{
+ try create_module.system_libs.put(arena, it.only_arg, .{
.needed = needed,
.weak = false,
.preferred_mode = lib_preferred_mode,
@@ -1796,16 +1847,16 @@ fn buildOutputType(
// Never mind what we're doing, just pass the args directly. For example --help.
return process.exit(try clangMain(arena, all_args));
},
- .pic => want_pic = true,
- .no_pic => want_pic = false,
- .pie => want_pie = true,
- .no_pie => want_pie = false,
- .lto => want_lto = true,
- .no_lto => want_lto = false,
- .red_zone => want_red_zone = true,
- .no_red_zone => want_red_zone = false,
- .omit_frame_pointer => omit_frame_pointer = true,
- .no_omit_frame_pointer => omit_frame_pointer = false,
+ .pic => mod_opts.pic = true,
+ .no_pic => mod_opts.pic = false,
+ .pie => create_module.opts.pie = true,
+ .no_pie => create_module.opts.pie = false,
+ .lto => create_module.opts.lto = true,
+ .no_lto => create_module.opts.lto = false,
+ .red_zone => mod_opts.red_zone = true,
+ .no_red_zone => mod_opts.red_zone = false,
+ .omit_frame_pointer => mod_opts.omit_frame_pointer = true,
+ .no_omit_frame_pointer => mod_opts.omit_frame_pointer = false,
.function_sections => function_sections = true,
.no_function_sections => function_sections = false,
.data_sections => data_sections = true,
@@ -1814,23 +1865,23 @@ fn buildOutputType(
.no_builtin => no_builtin = true,
.color_diagnostics => color = .on,
.no_color_diagnostics => color = .off,
- .stack_check => want_stack_check = true,
- .no_stack_check => want_stack_check = false,
+ .stack_check => mod_opts.stack_check = true,
+ .no_stack_check => mod_opts.stack_check = false,
.stack_protector => {
- if (want_stack_protector == null) {
- want_stack_protector = Compilation.default_stack_protector_buffer_size;
+ if (mod_opts.stack_protector == null) {
+ mod_opts.stack_protector = Compilation.default_stack_protector_buffer_size;
}
},
- .no_stack_protector => want_stack_protector = 0,
- .unwind_tables => want_unwind_tables = true,
- .no_unwind_tables => want_unwind_tables = false,
+ .no_stack_protector => mod_opts.stack_protector = 0,
+ .unwind_tables => mod_opts.unwind_tables = true,
+ .no_unwind_tables => mod_opts.unwind_tables = false,
.nostdlib => {
- ensure_libc_on_non_freestanding = false;
- ensure_libcpp_on_non_freestanding = false;
+ create_module.opts.ensure_libc_on_non_freestanding = false;
+ create_module.opts.ensure_libcpp_on_non_freestanding = false;
},
- .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false,
+ .nostdlib_cpp => create_module.opts.ensure_libcpp_on_non_freestanding = false,
.shared => {
- link_mode = .Dynamic;
+ create_module.opts.link_mode = .Dynamic;
is_shared_lib = true;
},
.rdynamic => rdynamic = true,
@@ -1870,7 +1921,7 @@ fn buildOutputType(
} else if (mem.eql(u8, linker_arg, "--no-as-needed")) {
needed = true;
} else if (mem.eql(u8, linker_arg, "-no-pie")) {
- want_pie = false;
+ create_module.opts.pie = false;
} else if (mem.eql(u8, linker_arg, "--sort-common")) {
// from ld.lld(1): --sort-common is ignored for GNU compatibility,
// this ignores plain --sort-common
@@ -1912,50 +1963,50 @@ fn buildOutputType(
if (mem.eql(u8, level, "s") or
mem.eql(u8, level, "z"))
{
- optimize_mode = .ReleaseSmall;
+ mod_opts.optimize_mode = .ReleaseSmall;
} else if (mem.eql(u8, level, "1") or
mem.eql(u8, level, "2") or
mem.eql(u8, level, "3") or
mem.eql(u8, level, "4") or
mem.eql(u8, level, "fast"))
{
- optimize_mode = .ReleaseFast;
+ mod_opts.optimize_mode = .ReleaseFast;
} else if (mem.eql(u8, level, "g") or
mem.eql(u8, level, "0"))
{
- optimize_mode = .Debug;
+ mod_opts.optimize_mode = .Debug;
} else {
- try clang_argv.appendSlice(it.other_args);
+ try clang_argv.appendSlice(arena, it.other_args);
}
},
.debug => {
- strip = false;
+ mod_opts.strip = false;
if (mem.eql(u8, it.only_arg, "g")) {
// We handled with strip = false above.
} else if (mem.eql(u8, it.only_arg, "g1") or
mem.eql(u8, it.only_arg, "gline-tables-only"))
{
// We handled with strip = false above. but we also want reduced debug info.
- try clang_argv.append("-gline-tables-only");
+ try clang_argv.append(arena, "-gline-tables-only");
} else {
- try clang_argv.appendSlice(it.other_args);
+ try clang_argv.appendSlice(arena, it.other_args);
}
},
.gdwarf32 => {
- strip = false;
+ mod_opts.strip = false;
dwarf_format = .@"32";
},
.gdwarf64 => {
- strip = false;
+ mod_opts.strip = false;
dwarf_format = .@"64";
},
.sanitize => {
if (mem.eql(u8, it.only_arg, "undefined")) {
- want_sanitize_c = true;
+ mod_opts.sanitize_c = true;
} else if (mem.eql(u8, it.only_arg, "thread")) {
- want_tsan = true;
+ mod_opts.sanitize_thread = true;
} else {
- try clang_argv.appendSlice(it.other_args);
+ try clang_argv.appendSlice(arena, it.other_args);
}
},
.linker_script => linker_script = it.only_arg,
@@ -1964,59 +2015,57 @@ fn buildOutputType(
// Have Clang print more infos, some tools such as CMake
// parse this to discover any implicit include and
// library dir to look-up into.
- try clang_argv.append("-v");
+ try clang_argv.append(arena, "-v");
},
.dry_run => {
// This flag means "dry run". Clang will not actually output anything
// to the file system.
verbose_link = true;
disable_c_depfile = true;
- try clang_argv.append("-###");
+ try clang_argv.append(arena, "-###");
},
.for_linker => try linker_args.append(it.only_arg),
.linker_input_z => {
try linker_args.append("-z");
try linker_args.append(it.only_arg);
},
- .lib_dir => try lib_dir_args.append(it.only_arg),
+ .lib_dir => try lib_dir_args.append(arena, it.only_arg),
.mcpu => target_mcpu = it.only_arg,
- .m => try llvm_m_args.append(it.only_arg),
+ .m => try create_module.llvm_m_args.append(arena, it.only_arg),
.dep_file => {
disable_c_depfile = true;
- try clang_argv.appendSlice(it.other_args);
+ try clang_argv.appendSlice(arena, it.other_args);
},
.dep_file_to_stdout => { // -M, -MM
// "Like -MD, but also implies -E and writes to stdout by default"
// "Like -MMD, but also implies -E and writes to stdout by default"
c_out_mode = .preprocessor;
disable_c_depfile = true;
- try clang_argv.appendSlice(it.other_args);
+ try clang_argv.appendSlice(arena, it.other_args);
},
- .framework_dir => try framework_dirs.append(it.only_arg),
- .framework => try frameworks.put(gpa, it.only_arg, .{}),
+ .framework_dir => try framework_dirs.append(arena, it.only_arg),
+ .framework => try frameworks.put(arena, it.only_arg, .{}),
.nostdlibinc => want_native_include_dirs = false,
- .strip => strip = true,
+ .strip => mod_opts.strip = true,
.exec_model => {
- wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, it.only_arg) orelse {
- fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{it.only_arg});
- };
+ create_module.opts.wasi_exec_model = parseWasiExecModel(it.only_arg);
},
.sysroot => {
sysroot = it.only_arg;
},
.entry => {
- entry = it.only_arg;
+ create_module.opts.entry = .{ .named = it.only_arg };
},
.force_undefined_symbol => {
- try force_undefined_symbols.put(gpa, it.only_arg, {});
+ try force_undefined_symbols.put(arena, it.only_arg, {});
},
- .weak_library => try system_libs.put(it.only_arg, .{
+ .weak_library => try create_module.system_libs.put(arena, it.only_arg, .{
.needed = false,
.weak = true,
.preferred_mode = lib_preferred_mode,
.search_strategy = lib_search_strategy,
}),
- .weak_framework => try frameworks.put(gpa, it.only_arg, .{ .weak = true }),
+ .weak_framework => try frameworks.put(arena, it.only_arg, .{ .weak = true }),
.headerpad_max_install_names => headerpad_max_install_names = true,
.compress_debug_sections => {
if (it.only_arg.len == 0) {
@@ -2077,14 +2126,14 @@ fn buildOutputType(
}
provided_name = name[prefix..end];
} else if (mem.eql(u8, arg, "-rpath")) {
- try rpath_list.append(linker_args_it.nextOrFatal());
+ try rpath_list.append(arena, linker_args_it.nextOrFatal());
} else if (mem.eql(u8, arg, "--subsystem")) {
subsystem = try parseSubSystem(linker_args_it.nextOrFatal());
} else if (mem.eql(u8, arg, "-I") or
mem.eql(u8, arg, "--dynamic-linker") or
mem.eql(u8, arg, "-dynamic-linker"))
{
- target_dynamic_linker = linker_args_it.nextOrFatal();
+ create_module.dynamic_linker = linker_args_it.nextOrFatal();
} else if (mem.eql(u8, arg, "-E") or
mem.eql(u8, arg, "--export-dynamic") or
mem.eql(u8, arg, "-export-dynamic"))
@@ -2145,9 +2194,9 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "-Bsymbolic")) {
linker_bind_global_refs_locally = true;
} else if (mem.eql(u8, arg, "--import-memory")) {
- linker_import_memory = true;
+ create_module.opts.import_memory = true;
} else if (mem.eql(u8, arg, "--export-memory")) {
- linker_export_memory = true;
+ create_module.opts.export_memory = true;
} else if (mem.eql(u8, arg, "--import-symbols")) {
linker_import_symbols = true;
} else if (mem.eql(u8, arg, "--import-table")) {
@@ -2155,7 +2204,7 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "--export-table")) {
linker_export_table = true;
} else if (mem.eql(u8, arg, "--no-entry")) {
- linker_force_entry = false;
+ create_module.opts.entry = .disabled;
} else if (mem.eql(u8, arg, "--initial-memory")) {
const next_arg = linker_args_it.nextOrFatal();
linker_initial_memory = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
@@ -2167,14 +2216,14 @@ fn buildOutputType(
fatal("unable to parse max memory size '{s}': {s}", .{ next_arg, @errorName(err) });
};
} else if (mem.eql(u8, arg, "--shared-memory")) {
- linker_shared_memory = true;
+ create_module.opts.shared_memory = true;
} else if (mem.eql(u8, arg, "--global-base")) {
const next_arg = linker_args_it.nextOrFatal();
linker_global_base = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
fatal("unable to parse global base '{s}': {s}", .{ next_arg, @errorName(err) });
};
} else if (mem.eql(u8, arg, "--export")) {
- try linker_export_symbol_names.append(linker_args_it.nextOrFatal());
+ try linker_export_symbol_names.append(arena, linker_args_it.nextOrFatal());
} else if (mem.eql(u8, arg, "--compress-debug-sections")) {
const arg1 = linker_args_it.nextOrFatal();
linker_compress_debug_sections = std.meta.stringToEnum(link.CompressDebugSections, arg1) orelse {
@@ -2232,9 +2281,9 @@ fn buildOutputType(
};
have_version = true;
} else if (mem.eql(u8, arg, "-e") or mem.eql(u8, arg, "--entry")) {
- entry = linker_args_it.nextOrFatal();
+ create_module.opts.entry = .{ .named = linker_args_it.nextOrFatal() };
} else if (mem.eql(u8, arg, "-u")) {
- try force_undefined_symbols.put(gpa, linker_args_it.nextOrFatal(), {});
+ try force_undefined_symbols.put(arena, linker_args_it.nextOrFatal(), {});
} else if (mem.eql(u8, arg, "--stack") or mem.eql(u8, arg, "-stack_size")) {
const stack_size = linker_args_it.nextOrFatal();
stack_size_override = std.fmt.parseUnsigned(u64, stack_size, 0) catch |err| {
@@ -2276,7 +2325,7 @@ fn buildOutputType(
{
// -s, --strip-all Strip all symbols
// -S, --strip-debug Strip debugging symbols
- strip = true;
+ mod_opts.strip = true;
} else if (mem.eql(u8, arg, "--start-group") or
mem.eql(u8, arg, "--end-group"))
{
@@ -2307,27 +2356,27 @@ fn buildOutputType(
fatal("unable to parse minor subsystem version '{s}': {s}", .{ minor, @errorName(err) });
};
} else if (mem.eql(u8, arg, "-framework")) {
- try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{});
+ try frameworks.put(arena, linker_args_it.nextOrFatal(), .{});
} else if (mem.eql(u8, arg, "-weak_framework")) {
- try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{ .weak = true });
+ try frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .weak = true });
} else if (mem.eql(u8, arg, "-needed_framework")) {
- try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{ .needed = true });
+ try frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .needed = true });
} else if (mem.eql(u8, arg, "-needed_library")) {
- try system_libs.put(linker_args_it.nextOrFatal(), .{
+ try create_module.system_libs.put(arena, linker_args_it.nextOrFatal(), .{
.weak = false,
.needed = true,
.preferred_mode = lib_preferred_mode,
.search_strategy = lib_search_strategy,
});
} else if (mem.startsWith(u8, arg, "-weak-l")) {
- try system_libs.put(arg["-weak-l".len..], .{
+ try create_module.system_libs.put(arena, arg["-weak-l".len..], .{
.weak = true,
.needed = false,
.preferred_mode = lib_preferred_mode,
.search_strategy = lib_search_strategy,
});
} else if (mem.eql(u8, arg, "-weak_library")) {
- try system_libs.put(linker_args_it.nextOrFatal(), .{
+ try create_module.system_libs.put(arena, linker_args_it.nextOrFatal(), .{
.weak = true,
.needed = false,
.preferred_mode = lib_preferred_mode,
@@ -2361,7 +2410,7 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "-install_name")) {
install_name = linker_args_it.nextOrFatal();
} else if (mem.eql(u8, arg, "-force_load")) {
- try link_objects.append(.{
+ try link_objects.append(arena, .{
.path = linker_args_it.nextOrFatal(),
.must_link = true,
});
@@ -2402,22 +2451,22 @@ fn buildOutputType(
}
}
- if (want_sanitize_c) |wsc| {
- if (wsc and optimize_mode == .ReleaseFast) {
- optimize_mode = .ReleaseSafe;
+ if (mod_opts.sanitize_c) |wsc| {
+ if (wsc and mod_opts.optimize_mode == .ReleaseFast) {
+ mod_opts.optimize_mode = .ReleaseSafe;
}
}
switch (c_out_mode) {
.link => {
- output_mode = if (is_shared_lib) .Lib else .Exe;
+ create_module.opts.output_mode = if (is_shared_lib) .Lib else .Exe;
emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out;
if (emit_llvm) {
fatal("-emit-llvm cannot be used when linking", .{});
}
},
.object => {
- output_mode = .Obj;
+ create_module.opts.output_mode = .Obj;
if (emit_llvm) {
emit_bin = .no;
if (out_path) |p| {
@@ -2434,7 +2483,7 @@ fn buildOutputType(
}
},
.assembly => {
- output_mode = .Obj;
+ create_module.opts.output_mode = .Obj;
emit_bin = .no;
if (emit_llvm) {
if (out_path) |p| {
@@ -2451,9 +2500,9 @@ fn buildOutputType(
}
},
.preprocessor => {
- output_mode = .Obj;
+ create_module.opts.output_mode = .Obj;
// An error message is generated when there is more than 1 C source file.
- if (c_source_files.items.len != 1) {
+ if (create_module.c_source_files.items.len != 1) {
// For example `zig cc` and no args should print the "no input files" message.
return process.exit(try clangMain(arena, all_args));
}
@@ -2465,7 +2514,7 @@ fn buildOutputType(
}
},
}
- if (c_source_files.items.len == 0 and
+ if (create_module.c_source_files.items.len == 0 and
link_objects.items.len == 0 and
root_src_file == null)
{
@@ -2476,258 +2525,72 @@ fn buildOutputType(
},
}
- {
- // Resolve module dependencies
- var it = modules.iterator();
- while (it.next()) |kv| {
- const deps_str = kv.value_ptr.deps_str;
- var deps_it = ModuleDepIterator.init(deps_str);
- while (deps_it.next()) |dep| {
- if (dep.expose.len == 0) {
- fatal("module '{s}' depends on '{s}' with a blank name", .{
- kv.key_ptr.*, dep.name,
- });
- }
-
- for ([_][]const u8{ "std", "root", "builtin" }) |name| {
- if (mem.eql(u8, dep.expose, name)) {
- fatal("unable to add module '{s}' under name '{s}': conflicts with builtin module", .{
- dep.name, dep.expose,
- });
- }
- }
-
- const dep_mod = modules.get(dep.name) orelse {
- fatal("module '{s}' depends on module '{s}' which does not exist", .{
- kv.key_ptr.*, dep.name,
- });
- };
-
- try kv.value_ptr.mod.deps.put(arena, dep.expose, dep_mod.mod);
- }
- }
- }
-
- if (arg_mode == .build and optimize_mode == .ReleaseSmall and strip == null)
- strip = true;
-
- if (arg_mode == .translate_c and c_source_files.items.len != 1) {
- fatal("translate-c expects exactly 1 source file (found {d})", .{c_source_files.items.len});
+ if (arg_mode == .translate_c and create_module.c_source_files.items.len != 1) {
+ fatal("translate-c expects exactly 1 source file (found {d})", .{create_module.c_source_files.items.len});
}
if (root_src_file == null and arg_mode == .zig_test) {
fatal("`zig test` expects a zig source file argument", .{});
}
- const root_name = if (provided_name) |n| n else blk: {
- if (arg_mode == .zig_test) {
- break :blk "test";
- } else if (root_src_file) |file| {
- const basename = fs.path.basename(file);
- break :blk basename[0 .. basename.len - fs.path.extension(basename).len];
- } else if (c_source_files.items.len >= 1) {
- const basename = fs.path.basename(c_source_files.items[0].src_path);
- break :blk basename[0 .. basename.len - fs.path.extension(basename).len];
- } else if (link_objects.items.len >= 1) {
- const basename = fs.path.basename(link_objects.items[0].path);
- break :blk basename[0 .. basename.len - fs.path.extension(basename).len];
- } else if (emit_bin == .yes) {
- const basename = fs.path.basename(emit_bin.yes);
- break :blk basename[0 .. basename.len - fs.path.extension(basename).len];
- } else if (rc_source_files.items.len >= 1) {
- const basename = fs.path.basename(rc_source_files.items[0].src_path);
- break :blk basename[0 .. basename.len - fs.path.extension(basename).len];
- } else if (res_files.items.len >= 1) {
- const basename = fs.path.basename(res_files.items[0].path);
- break :blk basename[0 .. basename.len - fs.path.extension(basename).len];
- } else if (show_builtin) {
- break :blk "builtin";
- } else if (arg_mode == .run) {
- fatal("`zig run` expects at least one positional argument", .{});
- // TODO once the attempt to unwrap error: LinkingWithoutZigSourceUnimplemented
- // is solved, remove the above fatal() and uncomment the `break` below.
- //break :blk "run";
- } else {
- fatal("expected a positional argument, -femit-bin=[path], --show-builtin, or --name [name]", .{});
- }
- };
-
- var target_parse_options: std.Target.Query.ParseOptions = .{
- .arch_os_abi = target_arch_os_abi,
- .cpu_features = target_mcpu,
- .dynamic_linker = target_dynamic_linker,
- .object_format = target_ofmt,
- };
-
- // Before passing the mcpu string in for parsing, we convert any -m flags that were
- // passed in via zig cc to zig-style.
- if (llvm_m_args.items.len != 0) {
- // If this returns null, we let it fall through to the case below which will
- // run the full parse function and do proper error handling.
- if (std.Target.Query.parseCpuArch(target_parse_options)) |cpu_arch| {
- var llvm_to_zig_name = std.StringHashMap([]const u8).init(gpa);
- defer llvm_to_zig_name.deinit();
-
- for (cpu_arch.allFeaturesList()) |feature| {
- const llvm_name = feature.llvm_name orelse continue;
- try llvm_to_zig_name.put(llvm_name, feature.name);
- }
-
- var mcpu_buffer = std.ArrayList(u8).init(gpa);
- defer mcpu_buffer.deinit();
-
- try mcpu_buffer.appendSlice(target_mcpu orelse "baseline");
-
- for (llvm_m_args.items) |llvm_m_arg| {
- if (mem.startsWith(u8, llvm_m_arg, "mno-")) {
- const llvm_name = llvm_m_arg["mno-".len..];
- const zig_name = llvm_to_zig_name.get(llvm_name) orelse {
- fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{
- @tagName(cpu_arch), llvm_name,
- });
- };
- try mcpu_buffer.append('-');
- try mcpu_buffer.appendSlice(zig_name);
- } else if (mem.startsWith(u8, llvm_m_arg, "m")) {
- const llvm_name = llvm_m_arg["m".len..];
- const zig_name = llvm_to_zig_name.get(llvm_name) orelse {
- fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{
- @tagName(cpu_arch), llvm_name,
- });
- };
- try mcpu_buffer.append('+');
- try mcpu_buffer.appendSlice(zig_name);
- } else {
- unreachable;
- }
- }
-
- const adjusted_target_mcpu = try arena.dupe(u8, mcpu_buffer.items);
- std.log.debug("adjusted target_mcpu: {s}", .{adjusted_target_mcpu});
- target_parse_options.cpu_features = adjusted_target_mcpu;
- }
- }
-
- const target_query = try parseTargetQueryOrReportFatalError(arena, target_parse_options);
- const target = try std.zig.system.resolveTargetQuery(target_query);
-
- if (target.os.tag != .freestanding) {
- if (ensure_libc_on_non_freestanding)
- link_libc = true;
- if (ensure_libcpp_on_non_freestanding)
- link_libcpp = true;
- }
-
- if (linker_force_entry) |force| {
- if (!force) {
- entry = null;
- } else if (entry == null and output_mode == .Exe) {
- entry = switch (target.ofmt) {
- .coff => "wWinMainCRTStartup",
- .macho => "_main",
- .elf, .plan9 => "_start",
- .wasm => defaultWasmEntryName(wasi_exec_model),
- else => |tag| fatal("No default entry point available for output format {s}", .{@tagName(tag)}),
- };
- }
- } else if (entry == null and target.isWasm() and output_mode == .Exe) {
- // For WebAssembly the compiler defaults to setting the entry name when no flags are set.
- entry = defaultWasmEntryName(wasi_exec_model);
- }
-
- if (target.ofmt == .coff) {
- // Now that we know the target supports resources,
- // we can add the res files as link objects.
- for (res_files.items) |res_file| {
- try link_objects.append(res_file);
- }
- } else {
- if (manifest_file != null) {
- fatal("manifest file is not allowed unless the target object format is coff (Windows/UEFI)", .{});
- }
- if (rc_source_files.items.len != 0) {
- fatal("rc files are not allowed unless the target object format is coff (Windows/UEFI)", .{});
- }
- if (res_files.items.len != 0) {
- fatal("res files are not allowed unless the target object format is coff (Windows/UEFI)", .{});
- }
- }
-
- if (target.cpu.arch.isWasm()) blk: {
- if (single_threaded == null) {
- single_threaded = true;
- }
- if (link_mode) |mode| {
- if (mode == .Dynamic) {
- if (linker_export_memory != null and linker_export_memory.?) {
- fatal("flags '-dynamic' and '--export-memory' are incompatible", .{});
- }
- // User did not supply `--export-memory` which is incompatible with -dynamic, therefore
- // set the flag to false to ensure it does not get enabled by default.
- linker_export_memory = false;
- }
- }
- if (wasi_exec_model != null and wasi_exec_model.? == .reactor) {
- if (entry) |entry_name| {
- if (!mem.eql(u8, "_initialize", entry_name)) {
- fatal("the entry symbol of the reactor model must be '_initialize', but found '{s}'", .{entry_name});
- }
- }
- }
- if (linker_shared_memory) {
- if (output_mode == .Obj) {
- fatal("shared memory is not allowed in object files", .{});
- }
-
- if (!target.cpu.features.isEnabled(@intFromEnum(std.Target.wasm.Feature.atomics)) or
- !target.cpu.features.isEnabled(@intFromEnum(std.Target.wasm.Feature.bulk_memory)))
- {
- fatal("'atomics' and 'bulk-memory' features must be enabled to use shared memory", .{});
- }
- break :blk;
- }
-
- // Single-threaded is the default for WebAssembly, so only when the user specified `-fno_single-threaded`
- // can they enable multithreaded WebAssembly builds.
- const is_single_threaded = single_threaded.?;
- if (!is_single_threaded) {
- fatal("'-fno-single-threaded' requires the linker feature shared-memory to be enabled using '--shared-memory'", .{});
+ if (root_src_file) |unresolved_src_path| {
+ if (create_module.modules.count() != 0) {
+ fatal("main module provided both by '--mod {s} {}{s}' and by positional argument '{s}'", .{
+ create_module.modules.keys()[0],
+ create_module.modules.values()[0].paths.root,
+ create_module.modules.values()[0].paths.root_src_path,
+ unresolved_src_path,
+ });
}
- }
- if (use_lld) |opt| {
- if (opt and target.isDarwin()) {
- fatal("LLD requested with Mach-O object format. Only the self-hosted linker is supported for this target.", .{});
- }
- }
+ // See duplicate logic: ModCreationGlobalFlags
+ create_module.opts.have_zcu = true;
+ if (mod_opts.single_threaded == false)
+ create_module.opts.any_non_single_threaded = true;
+ if (mod_opts.sanitize_thread == true)
+ create_module.opts.any_sanitize_thread = true;
+ if (mod_opts.unwind_tables == true)
+ create_module.opts.any_unwind_tables = true;
- if (want_lto) |opt| {
- if (opt and target.isDarwin()) {
- fatal("LTO is not yet supported with the Mach-O object format. More details: https://github.com/ziglang/zig/issues/8680", .{});
- }
+ const src_path = try introspect.resolvePath(arena, unresolved_src_path);
+ try create_module.modules.put(arena, "main", .{
+ .paths = .{
+ .root = .{
+ .root_dir = Cache.Directory.cwd(),
+ .sub_path = fs.path.dirname(src_path) orelse "",
+ },
+ .root_src_path = fs.path.basename(src_path),
+ },
+ .cc_argv = try clang_argv.toOwnedSlice(arena),
+ .inherited = mod_opts,
+ .target_arch_os_abi = target_arch_os_abi,
+ .target_mcpu = target_mcpu,
+ .deps = try deps.toOwnedSlice(arena),
+ .resolved = null,
+ .c_source_files_start = c_source_files_owner_index,
+ .c_source_files_end = create_module.c_source_files.items.len,
+ .rc_source_files_start = rc_source_files_owner_index,
+ .rc_source_files_end = create_module.rc_source_files.items.len,
+ });
+ cssan.reset();
+ mod_opts = .{};
+ target_arch_os_abi = null;
+ target_mcpu = null;
+ c_source_files_owner_index = create_module.c_source_files.items.len;
+ rc_source_files_owner_index = create_module.rc_source_files.items.len;
}
- if (comptime builtin.target.isDarwin()) {
- // If we want to link against frameworks, we need system headers.
- if (framework_dirs.items.len > 0 or frameworks.count() > 0)
- want_native_include_dirs = true;
+ if (c_source_files_owner_index != create_module.c_source_files.items.len) {
+ fatal("C source file '{s}' has no parent module", .{
+ create_module.c_source_files.items[c_source_files_owner_index].src_path,
+ });
}
- // Resolve the library path arguments with respect to sysroot.
- var lib_dirs = std.ArrayList([]const u8).init(arena);
- if (sysroot) |root| {
- for (lib_dir_args.items) |dir| {
- if (fs.path.isAbsolute(dir)) {
- const stripped_dir = dir[fs.path.diskDesignator(dir).len..];
- const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir });
- try lib_dirs.append(full_path);
- }
- try lib_dirs.append(dir);
- }
- } else {
- lib_dirs = lib_dir_args;
+ if (rc_source_files_owner_index != create_module.rc_source_files.items.len) {
+ fatal("resource file '{s}' has no parent module", .{
+ create_module.rc_source_files.items[rc_source_files_owner_index].src_path,
+ });
}
- lib_dir_args = undefined; // From here we use lib_dirs instead.
const self_exe_path: ?[]const u8 = if (!process.can_spawn)
null
@@ -2757,87 +2620,185 @@ fn buildOutputType(
};
defer zig_lib_directory.handle.close();
- // First, remove libc, libc++, and compiler_rt libraries from the system libraries list.
- // We need to know whether the set of system libraries contains anything besides these
- // to decide whether to trigger native path detection logic.
- var external_system_libs: std.MultiArrayList(struct {
- name: []const u8,
- info: SystemLib,
- }) = .{};
+ var global_cache_directory: Compilation.Directory = l: {
+ if (override_global_cache_dir) |p| {
+ break :l .{
+ .handle = try fs.cwd().makeOpenPath(p, .{}),
+ .path = p,
+ };
+ }
+ if (builtin.os.tag == .wasi) {
+ break :l getWasiPreopen("/cache");
+ }
+ const p = try introspect.resolveGlobalCacheDir(arena);
+ break :l .{
+ .handle = try fs.cwd().makeOpenPath(p, .{}),
+ .path = p,
+ };
+ };
+ defer global_cache_directory.handle.close();
- var resolved_system_libs: std.MultiArrayList(struct {
- name: []const u8,
- lib: Compilation.SystemLib,
- }) = .{};
+ create_module.global_cache_directory = global_cache_directory;
+ create_module.opts.emit_llvm_ir = emit_llvm_ir != .no;
+ create_module.opts.emit_llvm_bc = emit_llvm_bc != .no;
+ create_module.opts.emit_bin = emit_bin != .no;
+ create_module.opts.c_source_files_len = create_module.c_source_files.items.len;
- var libc_installation: ?LibCInstallation = null;
- if (libc_paths_file) |paths_file| {
- libc_installation = LibCInstallation.parse(arena, paths_file, target) catch |err| {
- fatal("unable to parse libc paths file at path {s}: {s}", .{ paths_file, @errorName(err) });
- };
+ const main_mod = try createModule(gpa, arena, &create_module, 0, null, zig_lib_directory);
+ for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| {
+ if (cli_mod.resolved == null)
+ fatal("module '{s}' declared but not used", .{key});
}
- for (system_libs.keys(), system_libs.values()) |lib_name, info| {
- if (target.is_libc_lib_name(lib_name)) {
- link_libc = true;
- continue;
- }
- if (target.is_libcpp_lib_name(lib_name)) {
- link_libcpp = true;
- continue;
- }
- switch (target_util.classifyCompilerRtLibName(target, lib_name)) {
- .none => {},
- .only_libunwind, .both => {
- link_libunwind = true;
- continue;
- },
- .only_compiler_rt => {
- warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name});
- continue;
+ // When you're testing std, the main module is std. In that case,
+ // we'll just set the std module to the main one, since avoiding
+ // the errors caused by duplicating it is more effort than it's
+ // worth.
+ const main_mod_is_std = m: {
+ const std_path = try fs.path.resolve(arena, &.{
+ zig_lib_directory.path orelse ".", "std", "std.zig",
+ });
+ const main_path = try fs.path.resolve(arena, &.{
+ main_mod.root.root_dir.path orelse ".",
+ main_mod.root.sub_path,
+ main_mod.root_src_path,
+ });
+ break :m mem.eql(u8, main_path, std_path);
+ };
+
+ const std_mod = m: {
+ if (main_mod_is_std) break :m main_mod;
+ if (create_module.modules.get("std")) |cli_mod| break :m cli_mod.resolved.?;
+
+ break :m try Package.Module.create(arena, .{
+ .global_cache_directory = global_cache_directory,
+ .paths = .{
+ .root = .{
+ .root_dir = zig_lib_directory,
+ .sub_path = "std",
+ },
+ .root_src_path = "std.zig",
},
- }
+ .fully_qualified_name = "std",
+ .cc_argv = &.{},
+ .inherited = .{},
+ .global = create_module.resolved_options,
+ .parent = main_mod,
+ .builtin_mod = main_mod.getBuiltinDependency(),
+ });
+ };
- if (target.isMinGW()) {
- const exists = mingw.libExists(arena, target, zig_lib_directory, lib_name) catch |err| {
- fatal("failed to check zig installation for DLL import libs: {s}", .{
- @errorName(err),
- });
- };
- if (exists) {
- try resolved_system_libs.append(arena, .{
- .name = lib_name,
- .lib = .{
- .needed = true,
- .weak = false,
- .path = null,
+ const root_mod = if (arg_mode == .zig_test) root_mod: {
+ const test_mod = if (test_runner_path) |test_runner| test_mod: {
+ const test_mod = try Package.Module.create(arena, .{
+ .global_cache_directory = global_cache_directory,
+ .paths = .{
+ .root = .{
+ .root_dir = Cache.Directory.cwd(),
+ .sub_path = fs.path.dirname(test_runner) orelse "",
},
- });
- continue;
- }
+ .root_src_path = fs.path.basename(test_runner),
+ },
+ .fully_qualified_name = "root",
+ .cc_argv = &.{},
+ .inherited = .{},
+ .global = create_module.resolved_options,
+ .parent = main_mod,
+ .builtin_mod = main_mod.getBuiltinDependency(),
+ });
+ test_mod.deps = try main_mod.deps.clone(arena);
+ break :test_mod test_mod;
+ } else try Package.Module.create(arena, .{
+ .global_cache_directory = global_cache_directory,
+ .paths = .{
+ .root = .{
+ .root_dir = zig_lib_directory,
+ },
+ .root_src_path = "test_runner.zig",
+ },
+ .fully_qualified_name = "root",
+ .cc_argv = &.{},
+ .inherited = .{},
+ .global = create_module.resolved_options,
+ .parent = main_mod,
+ .builtin_mod = main_mod.getBuiltinDependency(),
+ });
+
+ break :root_mod test_mod;
+ } else main_mod;
+
+ const target = main_mod.resolved_target.result;
+
+ if (target.ofmt != .coff) {
+ if (manifest_file != null) {
+ fatal("manifest file is not allowed unless the target object format is coff (Windows/UEFI)", .{});
+ }
+ if (create_module.rc_source_files.items.len != 0) {
+ fatal("rc files are not allowed unless the target object format is coff (Windows/UEFI)", .{});
}
+ if (contains_res_file) {
+ fatal("res files are not allowed unless the target object format is coff (Windows/UEFI)", .{});
+ }
+ }
- if (fs.path.isAbsolute(lib_name)) {
- fatal("cannot use absolute path as a system library: {s}", .{lib_name});
+ const root_name = if (provided_name) |n| n else blk: {
+ if (arg_mode == .zig_test) {
+ break :blk "test";
+ } else if (root_src_file) |file| {
+ const basename = fs.path.basename(file);
+ break :blk basename[0 .. basename.len - fs.path.extension(basename).len];
+ } else if (create_module.c_source_files.items.len >= 1) {
+ const basename = fs.path.basename(create_module.c_source_files.items[0].src_path);
+ break :blk basename[0 .. basename.len - fs.path.extension(basename).len];
+ } else if (link_objects.items.len >= 1) {
+ const basename = fs.path.basename(link_objects.items[0].path);
+ break :blk basename[0 .. basename.len - fs.path.extension(basename).len];
+ } else if (emit_bin == .yes) {
+ const basename = fs.path.basename(emit_bin.yes);
+ break :blk basename[0 .. basename.len - fs.path.extension(basename).len];
+ } else if (create_module.rc_source_files.items.len >= 1) {
+ const basename = fs.path.basename(create_module.rc_source_files.items[0].src_path);
+ break :blk basename[0 .. basename.len - fs.path.extension(basename).len];
+ } else if (show_builtin) {
+ break :blk "builtin";
+ } else if (arg_mode == .run) {
+ fatal("`zig run` expects at least one positional argument", .{});
+ // TODO once the attempt to unwrap error: LinkingWithoutZigSourceUnimplemented
+ // is solved, remove the above fatal() and uncomment the `break` below.
+ //break :blk "run";
+ } else {
+ fatal("expected a positional argument, -femit-bin=[path], --show-builtin, or --name [name]", .{});
}
+ };
- if (target.os.tag == .wasi) {
- if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| {
- try wasi_emulated_libs.append(crt_file);
- continue;
+ // Resolve the library path arguments with respect to sysroot.
+ var lib_dirs: std.ArrayListUnmanaged([]const u8) = .{};
+ if (sysroot) |root| {
+ try lib_dirs.ensureUnusedCapacity(arena, lib_dir_args.items.len * 2);
+ for (lib_dir_args.items) |dir| {
+ if (fs.path.isAbsolute(dir)) {
+ const stripped_dir = dir[fs.path.diskDesignator(dir).len..];
+ const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir });
+ lib_dirs.appendAssumeCapacity(full_path);
}
+ lib_dirs.appendAssumeCapacity(dir);
}
+ } else {
+ lib_dirs = lib_dir_args;
+ }
+ lib_dir_args = undefined; // From here we use lib_dirs instead.
- try external_system_libs.append(arena, .{
- .name = lib_name,
- .info = info,
- });
+ if (main_mod.resolved_target.is_native_os and target.isDarwin()) {
+ // If we want to link against frameworks, we need system headers.
+ if (framework_dirs.items.len > 0 or frameworks.count() > 0)
+ want_native_include_dirs = true;
}
- // After this point, external_system_libs is used instead of system_libs.
// Trigger native system library path detection if necessary.
- if (sysroot == null and target_query.isNativeOs() and target_query.isNativeAbi() and
- (external_system_libs.len != 0 or want_native_include_dirs))
+ if (sysroot == null and
+ main_mod.resolved_target.is_native_os and
+ main_mod.resolved_target.is_native_abi and
+ (create_module.external_system_libs.len != 0 or want_native_include_dirs))
{
const paths = std.zig.system.NativePaths.detect(arena, target) catch |err| {
fatal("unable to detect native system paths: {s}", .{@errorName(err)});
@@ -2846,20 +2807,27 @@ fn buildOutputType(
warn("{s}", .{warning});
}
- try clang_argv.ensureUnusedCapacity(paths.include_dirs.items.len * 2);
+ try clang_argv.ensureUnusedCapacity(arena, paths.include_dirs.items.len * 2);
for (paths.include_dirs.items) |include_dir| {
clang_argv.appendAssumeCapacity("-isystem");
clang_argv.appendAssumeCapacity(include_dir);
}
- try framework_dirs.appendSlice(paths.framework_dirs.items);
- try lib_dirs.appendSlice(paths.lib_dirs.items);
- try rpath_list.appendSlice(paths.rpaths.items);
+ try framework_dirs.appendSlice(arena, paths.framework_dirs.items);
+ try lib_dirs.appendSlice(arena, paths.lib_dirs.items);
+ try rpath_list.appendSlice(arena, paths.rpaths.items);
+ }
+
+ var libc_installation: ?LibCInstallation = null;
+ if (libc_paths_file) |paths_file| {
+ libc_installation = LibCInstallation.parse(arena, paths_file, target) catch |err| {
+ fatal("unable to parse libc paths file at path {s}: {s}", .{ paths_file, @errorName(err) });
+ };
}
if (builtin.target.os.tag == .windows and
target.abi == .msvc and
- external_system_libs.len != 0)
+ create_module.external_system_libs.len != 0)
{
if (libc_installation == null) {
libc_installation = try LibCInstallation.findNative(.{
@@ -2868,7 +2836,10 @@ fn buildOutputType(
.target = target,
});
- try lib_dirs.appendSlice(&.{ libc_installation.?.msvc_lib_dir.?, libc_installation.?.kernel32_lib_dir.? });
+ try lib_dirs.appendSlice(arena, &.{
+ libc_installation.?.msvc_lib_dir.?,
+ libc_installation.?.kernel32_lib_dir.?,
+ });
}
}
@@ -2888,7 +2859,7 @@ fn buildOutputType(
preferred_mode: std.builtin.LinkMode,
}).init(arena);
- syslib: for (external_system_libs.items(.name), external_system_libs.items(.info)) |lib_name, info| {
+ syslib: for (create_module.external_system_libs.items(.name), create_module.external_system_libs.items(.info)) |lib_name, info| {
// Checked in the first pass above while looking for libc libraries.
assert(!fs.path.isAbsolute(lib_name));
@@ -2908,8 +2879,8 @@ fn buildOutputType(
)) {
const path = try arena.dupe(u8, test_path.items);
switch (info.preferred_mode) {
- .Static => try link_objects.append(.{ .path = path }),
- .Dynamic => try resolved_system_libs.append(arena, .{
+ .Static => try link_objects.append(arena, .{ .path = path }),
+ .Dynamic => try create_module.resolved_system_libs.append(arena, .{
.name = lib_name,
.lib = .{
.needed = info.needed,
@@ -2942,8 +2913,8 @@ fn buildOutputType(
)) {
const path = try arena.dupe(u8, test_path.items);
switch (info.fallbackMode()) {
- .Static => try link_objects.append(.{ .path = path }),
- .Dynamic => try resolved_system_libs.append(arena, .{
+ .Static => try link_objects.append(arena, .{ .path = path }),
+ .Dynamic => try create_module.resolved_system_libs.append(arena, .{
.name = lib_name,
.lib = .{
.needed = info.needed,
@@ -2976,8 +2947,8 @@ fn buildOutputType(
)) {
const path = try arena.dupe(u8, test_path.items);
switch (info.preferred_mode) {
- .Static => try link_objects.append(.{ .path = path }),
- .Dynamic => try resolved_system_libs.append(arena, .{
+ .Static => try link_objects.append(arena, .{ .path = path }),
+ .Dynamic => try create_module.resolved_system_libs.append(arena, .{
.name = lib_name,
.lib = .{
.needed = info.needed,
@@ -3000,8 +2971,8 @@ fn buildOutputType(
)) {
const path = try arena.dupe(u8, test_path.items);
switch (info.fallbackMode()) {
- .Static => try link_objects.append(.{ .path = path }),
- .Dynamic => try resolved_system_libs.append(arena, .{
+ .Static => try link_objects.append(arena, .{ .path = path }),
+ .Dynamic => try create_module.resolved_system_libs.append(arena, .{
.name = lib_name,
.lib = .{
.needed = info.needed,
@@ -3035,7 +3006,8 @@ fn buildOutputType(
process.exit(1);
}
}
- // After this point, resolved_system_libs is used instead of external_system_libs.
+ // After this point, create_module.resolved_system_libs is used instead of
+ // create_module.external_system_libs.
// We now repeat part of the process for frameworks.
var resolved_frameworks = std.ArrayList(Compilation.Framework).init(arena);
@@ -3090,10 +3062,10 @@ fn buildOutputType(
}
// After this point, resolved_frameworks is used instead of frameworks.
- if (output_mode == .Obj and (target.ofmt == .coff or target.ofmt == .macho)) {
- const total_obj_count = c_source_files.items.len +
+ if (create_module.opts.output_mode == .Obj and (target.ofmt == .coff or target.ofmt == .macho)) {
+ const total_obj_count = create_module.c_source_files.items.len +
@intFromBool(root_src_file != null) +
- rc_source_files.items.len +
+ create_module.rc_source_files.items.len +
link_objects.items.len;
if (total_obj_count > 1) {
fatal("{s} does not support linking multiple objects into one", .{@tagName(target.ofmt)});
@@ -3141,8 +3113,8 @@ fn buildOutputType(
.basename = try std.zig.binNameAlloc(arena, .{
.root_name = root_name,
.target = target,
- .output_mode = output_mode,
- .link_mode = link_mode,
+ .output_mode = create_module.opts.output_mode,
+ .link_mode = create_module.opts.link_mode,
.version = optional_version,
}),
},
@@ -3260,9 +3232,9 @@ fn buildOutputType(
};
defer emit_docs_resolved.deinit();
- const is_exe_or_dyn_lib = switch (output_mode) {
+ const is_exe_or_dyn_lib = switch (create_module.opts.output_mode) {
.Obj => false,
- .Lib => (link_mode orelse .Static) == .Dynamic,
+ .Lib => (create_module.opts.link_mode orelse .Static) == .Dynamic,
.Exe => true,
};
// Note that cmake when targeting Windows will try to execute
@@ -3294,76 +3266,10 @@ fn buildOutputType(
};
defer emit_implib_resolved.deinit();
- const main_mod: ?*Package.Module = if (root_src_file) |unresolved_src_path| blk: {
- const src_path = try introspect.resolvePath(arena, unresolved_src_path);
- if (main_mod_path) |unresolved_main_mod_path| {
- const p = try introspect.resolvePath(arena, unresolved_main_mod_path);
- break :blk try Package.Module.create(arena, .{
- .root = .{
- .root_dir = Cache.Directory.cwd(),
- .sub_path = p,
- },
- .root_src_path = if (p.len == 0)
- src_path
- else
- try fs.path.relative(arena, p, src_path),
- .fully_qualified_name = "root",
- });
- } else {
- break :blk try Package.Module.create(arena, .{
- .root = .{
- .root_dir = Cache.Directory.cwd(),
- .sub_path = fs.path.dirname(src_path) orelse "",
- },
- .root_src_path = fs.path.basename(src_path),
- .fully_qualified_name = "root",
- });
- }
- } else null;
-
- // Transfer packages added with --deps to the root package
- if (main_mod) |mod| {
- var it = ModuleDepIterator.init(root_deps_str orelse "");
- while (it.next()) |dep| {
- if (dep.expose.len == 0) {
- fatal("root module depends on '{s}' with a blank name", .{dep.name});
- }
-
- for ([_][]const u8{ "std", "root", "builtin" }) |name| {
- if (mem.eql(u8, dep.expose, name)) {
- fatal("unable to add module '{s}' under name '{s}': conflicts with builtin module", .{ dep.name, dep.expose });
- }
- }
-
- const dep_mod = modules.get(dep.name) orelse
- fatal("root module depends on module '{s}' which does not exist", .{dep.name});
-
- try mod.deps.put(arena, dep.expose, dep_mod.mod);
- }
- }
-
var thread_pool: ThreadPool = undefined;
try thread_pool.init(.{ .allocator = gpa });
defer thread_pool.deinit();
- var global_cache_directory: Compilation.Directory = l: {
- if (override_global_cache_dir) |p| {
- break :l .{
- .handle = try fs.cwd().makeOpenPath(p, .{}),
- .path = p,
- };
- }
- if (builtin.os.tag == .wasi) {
- break :l getWasiPreopen("/cache");
- }
- const p = try introspect.resolveGlobalCacheDir(arena);
- break :l .{
- .handle = try fs.cwd().makeOpenPath(p, .{}),
- .path = p,
- };
- };
- defer global_cache_directory.handle.close();
-
var cleanup_local_cache_dir: ?fs.Dir = null;
defer if (cleanup_local_cache_dir) |*dir| dir.close();
@@ -3379,37 +3285,37 @@ fn buildOutputType(
if (arg_mode == .run) {
break :l global_cache_directory;
}
- if (main_mod != null) {
- // search upwards from cwd until we find directory with build.zig
- const cwd_path = try process.getCwdAlloc(arena);
- const zig_cache = "zig-cache";
- var dirname: []const u8 = cwd_path;
- while (true) {
- const joined_path = try fs.path.join(arena, &.{
- dirname, Package.build_zig_basename,
- });
- if (fs.cwd().access(joined_path, .{})) |_| {
- const cache_dir_path = try fs.path.join(arena, &.{ dirname, zig_cache });
- const dir = try fs.cwd().makeOpenPath(cache_dir_path, .{});
- cleanup_local_cache_dir = dir;
- break :l .{ .handle = dir, .path = cache_dir_path };
- } else |err| switch (err) {
- error.FileNotFound => {
- dirname = fs.path.dirname(dirname) orelse {
- break :l global_cache_directory;
- };
- continue;
- },
- else => break :l global_cache_directory,
- }
+
+ // search upwards from cwd until we find directory with build.zig
+ const cwd_path = try process.getCwdAlloc(arena);
+ const zig_cache = "zig-cache";
+ var dirname: []const u8 = cwd_path;
+ while (true) {
+ const joined_path = try fs.path.join(arena, &.{
+ dirname, Package.build_zig_basename,
+ });
+ if (fs.cwd().access(joined_path, .{})) |_| {
+ const cache_dir_path = try fs.path.join(arena, &.{ dirname, zig_cache });
+ const dir = try fs.cwd().makeOpenPath(cache_dir_path, .{});
+ cleanup_local_cache_dir = dir;
+ break :l .{ .handle = dir, .path = cache_dir_path };
+ } else |err| switch (err) {
+ error.FileNotFound => {
+ dirname = fs.path.dirname(dirname) orelse {
+ break :l global_cache_directory;
+ };
+ continue;
+ },
+ else => break :l global_cache_directory,
}
}
+
// Otherwise we really don't have a reasonable place to put the local cache directory,
// so we utilize the global one.
break :l global_cache_directory;
};
- for (c_source_files.items) |*src| {
+ for (create_module.c_source_files.items) |*src| {
if (!mem.eql(u8, src.src_path, "-")) continue;
const ext = src.ext orelse
@@ -3452,13 +3358,14 @@ fn buildOutputType(
.zig_lib_directory = zig_lib_directory,
.local_cache_directory = local_cache_directory,
.global_cache_directory = global_cache_directory,
+ .thread_pool = &thread_pool,
+ .self_exe_path = self_exe_path,
+ .config = create_module.resolved_options,
.root_name = root_name,
- .target = target,
- .is_native_os = target_query.isNativeOs(),
- .is_native_abi = target_query.isNativeAbi(),
.sysroot = sysroot,
- .output_mode = output_mode,
.main_mod = main_mod,
+ .root_mod = root_mod,
+ .std_mod = std_mod,
.emit_bin = emit_bin_loc,
.emit_h = emit_h_resolved.data,
.emit_asm = emit_asm_resolved.data,
@@ -3466,43 +3373,22 @@ fn buildOutputType(
.emit_llvm_bc = emit_llvm_bc_resolved.data,
.emit_docs = emit_docs_resolved.data,
.emit_implib = emit_implib_resolved.data,
- .link_mode = link_mode,
.dll_export_fns = dll_export_fns,
- .optimize_mode = optimize_mode,
.keep_source_files_loaded = false,
- .clang_argv = clang_argv.items,
.lib_dirs = lib_dirs.items,
.rpath_list = rpath_list.items,
.symbol_wrap_set = symbol_wrap_set,
- .c_source_files = c_source_files.items,
- .rc_source_files = rc_source_files.items,
+ .c_source_files = create_module.c_source_files.items,
+ .rc_source_files = create_module.rc_source_files.items,
.manifest_file = manifest_file,
.rc_includes = rc_includes,
.link_objects = link_objects.items,
.framework_dirs = framework_dirs.items,
.frameworks = resolved_frameworks.items,
- .system_lib_names = resolved_system_libs.items(.name),
- .system_lib_infos = resolved_system_libs.items(.lib),
- .wasi_emulated_libs = wasi_emulated_libs.items,
- .link_libc = link_libc,
- .link_libcpp = link_libcpp,
- .link_libunwind = link_libunwind,
- .want_pic = want_pic,
- .want_pie = want_pie,
- .want_lto = want_lto,
- .want_unwind_tables = want_unwind_tables,
- .want_sanitize_c = want_sanitize_c,
- .want_stack_check = want_stack_check,
- .want_stack_protector = want_stack_protector,
- .want_red_zone = want_red_zone,
- .omit_frame_pointer = omit_frame_pointer,
- .want_valgrind = want_valgrind,
- .want_tsan = want_tsan,
+ .system_lib_names = create_module.resolved_system_libs.items(.name),
+ .system_lib_infos = create_module.resolved_system_libs.items(.lib),
+ .wasi_emulated_libs = create_module.wasi_emulated_libs.items,
.want_compiler_rt = want_compiler_rt,
- .use_llvm = use_llvm,
- .use_lib_llvm = use_lib_llvm,
- .use_lld = use_lld,
- .use_clang = use_clang,
.hash_style = hash_style,
.rdynamic = rdynamic,
.linker_script = linker_script,
@@ -3513,14 +3399,11 @@ fn buildOutputType(
.linker_gc_sections = linker_gc_sections,
.linker_allow_shlib_undefined = linker_allow_shlib_undefined,
.linker_bind_global_refs_locally = linker_bind_global_refs_locally,
- .linker_import_memory = linker_import_memory,
- .linker_export_memory = linker_export_memory,
.linker_import_symbols = linker_import_symbols,
.linker_import_table = linker_import_table,
.linker_export_table = linker_export_table,
.linker_initial_memory = linker_initial_memory,
.linker_max_memory = linker_max_memory,
- .linker_shared_memory = linker_shared_memory,
.linker_print_gc_sections = linker_print_gc_sections,
.linker_print_icf_sections = linker_print_icf_sections,
.linker_print_map = linker_print_map,
@@ -3546,18 +3429,13 @@ fn buildOutputType(
.minor_subsystem_version = minor_subsystem_version,
.link_eh_frame_hdr = link_eh_frame_hdr,
.link_emit_relocs = link_emit_relocs,
- .entry = entry,
.force_undefined_symbols = force_undefined_symbols,
.stack_size_override = stack_size_override,
.image_base_override = image_base_override,
- .strip = strip,
.formatted_panics = formatted_panics,
- .single_threaded = single_threaded,
.function_sections = function_sections,
.data_sections = data_sections,
.no_builtin = no_builtin,
- .self_exe_path = self_exe_path,
- .thread_pool = &thread_pool,
.clang_passthrough_mode = clang_passthrough_mode,
.clang_preprocessor_mode = clang_preprocessor_mode,
.version = optional_version,
@@ -3571,21 +3449,17 @@ fn buildOutputType(
.verbose_llvm_bc = verbose_llvm_bc,
.verbose_cimport = verbose_cimport,
.verbose_llvm_cpu_features = verbose_llvm_cpu_features,
- .machine_code_model = machine_code_model,
.color = color,
.time_report = time_report,
.stack_report = stack_report,
- .is_test = arg_mode == .zig_test,
.each_lib_rpath = each_lib_rpath,
.build_id = build_id,
- .test_evented_io = test_evented_io,
.test_filter = test_filter,
.test_name_prefix = test_name_prefix,
.test_runner_path = test_runner_path,
.disable_lld_caching = !output_to_cache,
.subsystem = subsystem,
.dwarf_format = dwarf_format,
- .wasi_exec_model = wasi_exec_model,
.debug_compile_errors = debug_compile_errors,
.enable_link_snapshots = enable_link_snapshots,
.install_name = install_name,
@@ -3595,7 +3469,6 @@ fn buildOutputType(
.headerpad_max_install_names = headerpad_max_install_names,
.dead_strip_dylibs = dead_strip_dylibs,
.reference_trace = reference_trace,
- .error_tracing = error_tracing,
.pdb_out_path = pdb_out_path,
.error_limit = error_limit,
.want_structured_cfg = want_structured_cfg,
@@ -3702,7 +3575,7 @@ fn buildOutputType(
try test_exec_args.appendSlice(&.{ "-I", p });
}
- if (link_libc) {
+ if (create_module.resolved_options.link_libc) {
try test_exec_args.append("-lc");
} else if (target.os.tag == .windows) {
try test_exec_args.appendSlice(&.{
@@ -3711,14 +3584,15 @@ fn buildOutputType(
});
}
- if (!mem.eql(u8, target_arch_os_abi, "native")) {
+ const first_cli_mod = create_module.modules.values()[0];
+ if (first_cli_mod.target_arch_os_abi) |triple| {
try test_exec_args.append("-target");
- try test_exec_args.append(target_arch_os_abi);
+ try test_exec_args.append(triple);
}
- if (target_mcpu) |mcpu| {
+ if (first_cli_mod.target_mcpu) |mcpu| {
try test_exec_args.append(try std.fmt.allocPrint(arena, "-mcpu={s}", .{mcpu}));
}
- if (target_dynamic_linker) |dl| {
+ if (create_module.dynamic_linker) |dl| {
try test_exec_args.append("--dynamic-linker");
try test_exec_args.append(dl);
}
@@ -3742,7 +3616,7 @@ fn buildOutputType(
&comp_destroyed,
all_args,
runtime_args_start,
- link_libc,
+ create_module.resolved_options.link_libc,
);
}
@@ -3750,6 +3624,243 @@ fn buildOutputType(
return cleanExit();
}
+const CreateModule = struct {
+ global_cache_directory: Cache.Directory,
+ modules: std.StringArrayHashMapUnmanaged(CliModule),
+ opts: Compilation.Config.Options,
+ dynamic_linker: ?[]const u8,
+ object_format: ?[]const u8,
+ /// undefined until createModule() for the root module is called.
+ resolved_options: Compilation.Config,
+
+ /// This one is used while collecting CLI options. The set of libs is used
+ /// directly after computing the target and used to compute link_libc,
+ /// link_libcpp, and then the libraries are filtered into
+ /// `external_system_libs` and `resolved_system_libs`.
+ system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
+ external_system_libs: std.MultiArrayList(struct {
+ name: []const u8,
+ info: SystemLib,
+ }),
+ resolved_system_libs: std.MultiArrayList(struct {
+ name: []const u8,
+ lib: Compilation.SystemLib,
+ }),
+ wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CRTFile),
+
+ c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile),
+ rc_source_files: std.ArrayListUnmanaged(Compilation.RcSourceFile),
+
+ // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
+ // This array is populated by zig cc frontend and then has to be converted to zig-style
+ // CPU features.
+ llvm_m_args: std.ArrayListUnmanaged([]const u8),
+};
+
+fn createModule(
+ gpa: Allocator,
+ arena: Allocator,
+ create_module: *CreateModule,
+ index: usize,
+ parent: ?*Package.Module,
+ zig_lib_directory: Cache.Directory,
+) Allocator.Error!*Package.Module {
+ const cli_mod = &create_module.modules.values()[index];
+ if (cli_mod.resolved) |m| return m;
+
+ const name = create_module.modules.keys()[index];
+
+ cli_mod.inherited.resolved_target = t: {
+ // If the target is not overridden, use the parent's target. Of course,
+ // if this is the root module then we need to proceed to resolve the
+ // target.
+ if (cli_mod.target_arch_os_abi == null and
+ cli_mod.target_mcpu == null and
+ create_module.dynamic_linker == null and
+ create_module.object_format == null)
+ {
+ if (parent) |p| break :t p.resolved_target;
+ }
+
+ var target_parse_options: std.Target.Query.ParseOptions = .{
+ .arch_os_abi = cli_mod.target_arch_os_abi orelse "native",
+ .cpu_features = cli_mod.target_mcpu,
+ .dynamic_linker = create_module.dynamic_linker,
+ .object_format = create_module.object_format,
+ };
+
+ // Before passing the mcpu string in for parsing, we convert any -m flags that were
+ // passed in via zig cc to zig-style.
+ if (create_module.llvm_m_args.items.len != 0) {
+ // If this returns null, we let it fall through to the case below which will
+ // run the full parse function and do proper error handling.
+ if (std.Target.Query.parseCpuArch(target_parse_options)) |cpu_arch| {
+ var llvm_to_zig_name = std.StringHashMap([]const u8).init(gpa);
+ defer llvm_to_zig_name.deinit();
+
+ for (cpu_arch.allFeaturesList()) |feature| {
+ const llvm_name = feature.llvm_name orelse continue;
+ try llvm_to_zig_name.put(llvm_name, feature.name);
+ }
+
+ var mcpu_buffer = std.ArrayList(u8).init(gpa);
+ defer mcpu_buffer.deinit();
+
+ try mcpu_buffer.appendSlice(cli_mod.target_mcpu orelse "baseline");
+
+ for (create_module.llvm_m_args.items) |llvm_m_arg| {
+ if (mem.startsWith(u8, llvm_m_arg, "mno-")) {
+ const llvm_name = llvm_m_arg["mno-".len..];
+ const zig_name = llvm_to_zig_name.get(llvm_name) orelse {
+ fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{
+ @tagName(cpu_arch), llvm_name,
+ });
+ };
+ try mcpu_buffer.append('-');
+ try mcpu_buffer.appendSlice(zig_name);
+ } else if (mem.startsWith(u8, llvm_m_arg, "m")) {
+ const llvm_name = llvm_m_arg["m".len..];
+ const zig_name = llvm_to_zig_name.get(llvm_name) orelse {
+ fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{
+ @tagName(cpu_arch), llvm_name,
+ });
+ };
+ try mcpu_buffer.append('+');
+ try mcpu_buffer.appendSlice(zig_name);
+ } else {
+ unreachable;
+ }
+ }
+
+ const adjusted_target_mcpu = try arena.dupe(u8, mcpu_buffer.items);
+ std.log.debug("adjusted target_mcpu: {s}", .{adjusted_target_mcpu});
+ target_parse_options.cpu_features = adjusted_target_mcpu;
+ }
+ }
+
+ const target_query = parseTargetQueryOrReportFatalError(arena, target_parse_options);
+ const target = resolveTargetQueryOrFatal(target_query);
+ break :t .{
+ .result = target,
+ .is_native_os = target_query.isNativeOs(),
+ .is_native_abi = target_query.isNativeAbi(),
+ };
+ };
+
+ if (parent == null) {
+ // This block is for initializing the fields of
+ // `Compilation.Config.Options` that require knowledge of the
+ // target (which was just now resolved for the root module above).
+ const resolved_target = cli_mod.inherited.resolved_target.?;
+ create_module.opts.resolved_target = resolved_target;
+ create_module.opts.root_optimize_mode = cli_mod.inherited.optimize_mode;
+ const target = resolved_target.result;
+
+ // First, remove libc, libc++, and compiler_rt libraries from the system libraries list.
+ // We need to know whether the set of system libraries contains anything besides these
+ // to decide whether to trigger native path detection logic.
+ for (create_module.system_libs.keys(), create_module.system_libs.values()) |lib_name, info| {
+ if (target.is_libc_lib_name(lib_name)) {
+ create_module.opts.link_libc = true;
+ continue;
+ }
+ if (target.is_libcpp_lib_name(lib_name)) {
+ create_module.opts.link_libcpp = true;
+ continue;
+ }
+ switch (target_util.classifyCompilerRtLibName(target, lib_name)) {
+ .none => {},
+ .only_libunwind, .both => {
+ create_module.opts.link_libunwind = true;
+ continue;
+ },
+ .only_compiler_rt => {
+ warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name});
+ continue;
+ },
+ }
+
+ if (target.isMinGW()) {
+ const exists = mingw.libExists(arena, target, zig_lib_directory, lib_name) catch |err| {
+ fatal("failed to check zig installation for DLL import libs: {s}", .{
+ @errorName(err),
+ });
+ };
+ if (exists) {
+ try create_module.resolved_system_libs.append(arena, .{
+ .name = lib_name,
+ .lib = .{
+ .needed = true,
+ .weak = false,
+ .path = null,
+ },
+ });
+ continue;
+ }
+ }
+
+ if (fs.path.isAbsolute(lib_name)) {
+ fatal("cannot use absolute path as a system library: {s}", .{lib_name});
+ }
+
+ if (target.os.tag == .wasi) {
+ if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| {
+ try create_module.wasi_emulated_libs.append(arena, crt_file);
+ continue;
+ }
+ }
+
+ try create_module.external_system_libs.append(arena, .{
+ .name = lib_name,
+ .info = info,
+ });
+ }
+ // After this point, external_system_libs is used instead of system_libs.
+
+ create_module.resolved_options = Compilation.Config.resolve(create_module.opts) catch |err| switch (err) {
+ else => fatal("unable to resolve compilation options: {s}", .{@errorName(err)}),
+ };
+ }
+
+ const mod = Package.Module.create(arena, .{
+ .global_cache_directory = create_module.global_cache_directory,
+ .paths = cli_mod.paths,
+ .fully_qualified_name = name,
+
+ .cc_argv = cli_mod.cc_argv,
+ .inherited = cli_mod.inherited,
+ .global = create_module.resolved_options,
+ .parent = parent,
+ .builtin_mod = null,
+ }) catch |err| switch (err) {
+ error.ValgrindUnsupportedOnTarget => fatal("unable to create module '{s}': valgrind does not support the selected target CPU architecture", .{name}),
+ error.TargetRequiresSingleThreaded => fatal("unable to create module '{s}': the selected target does not support multithreading", .{name}),
+ error.BackendRequiresSingleThreaded => fatal("unable to create module '{s}': the selected machine code backend is limited to single-threaded applications", .{name}),
+ error.TargetRequiresPic => fatal("unable to create module '{s}': the selected target requires position independent code", .{name}),
+ error.PieRequiresPic => fatal("unable to create module '{s}': making a Position Independent Executable requires enabling Position Independent Code", .{name}),
+ error.DynamicLinkingRequiresPic => fatal("unable to create module '{s}': dynamic linking requires enabling Position Independent Code", .{name}),
+ error.TargetHasNoRedZone => fatal("unable to create module '{s}': the selected target does not have a red zone", .{name}),
+ error.StackCheckUnsupportedByTarget => fatal("unable to create module '{s}': the selected target does not support stack checking", .{name}),
+ error.StackProtectorUnsupportedByTarget => fatal("unable to create module '{s}': the selected target does not support stack protection", .{name}),
+ error.StackProtectorUnavailableWithoutLibC => fatal("unable to create module '{s}': enabling stack protection requires libc", .{name}),
+ error.OutOfMemory => return error.OutOfMemory,
+ };
+ cli_mod.resolved = mod;
+
+ for (create_module.c_source_files.items[cli_mod.c_source_files_start..cli_mod.c_source_files_end]) |*item| item.owner = mod;
+
+ for (create_module.rc_source_files.items[cli_mod.rc_source_files_start..cli_mod.rc_source_files_end]) |*item| item.owner = mod;
+
+ for (cli_mod.deps) |dep| {
+ const dep_index = create_module.modules.getIndex(dep.key) orelse
+ fatal("module '{s}' depends on non-existent module '{s}'", .{ name, dep.key });
+ const dep_mod = try createModule(gpa, arena, create_module, dep_index, mod, zig_lib_directory);
+ try mod.deps.put(arena, dep.key, dep_mod);
+ }
+
+ return mod;
+}
+
fn saveState(comp: *Compilation, debug_incremental: bool) void {
if (debug_incremental) {
comp.saveState() catch |err| {
@@ -3984,36 +4095,10 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void {
}
}
-const ModuleDepIterator = struct {
- split: mem.SplitIterator(u8, .scalar),
-
- fn init(deps_str: []const u8) ModuleDepIterator {
- return .{ .split = mem.splitScalar(u8, deps_str, ',') };
- }
-
- const Dependency = struct {
- expose: []const u8,
- name: []const u8,
- };
-
- fn next(it: *ModuleDepIterator) ?Dependency {
- if (it.split.buffer.len == 0) return null; // don't return "" for the first iteration on ""
- const str = it.split.next() orelse return null;
- if (mem.indexOfScalar(u8, str, '=')) |i| {
- return .{
- .expose = str[0..i],
- .name = str[i + 1 ..],
- };
- } else {
- return .{ .expose = str, .name = str };
- }
- }
-};
-
fn parseTargetQueryOrReportFatalError(
allocator: Allocator,
opts: std.Target.Query.ParseOptions,
-) !std.Target.Query {
+) std.Target.Query {
var opts_with_diags = opts;
var diags: std.Target.Query.ParseOptions.Diagnostics = .{};
if (opts_with_diags.diagnostics == null) {
@@ -4057,7 +4142,9 @@ fn parseTargetQueryOrReportFatalError(
}
fatal("unknown object format: '{s}'", .{opts.object_format.?});
},
- else => |e| return e,
+ else => |e| fatal("unable to parse target query '{s}': {s}", .{
+ opts.arch_os_abi, @errorName(e),
+ }),
};
}
@@ -4667,7 +4754,7 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes:
.os_tag = .windows,
.abi = .msvc,
};
- const target = try std.zig.system.resolveTargetQuery(target_query);
+ const target = resolveTargetQueryOrFatal(target_query);
const is_native_abi = target_query.isNativeAbi();
const detected_libc = Compilation.detectLibCIncludeDirs(arena, zig_lib_dir, target, is_native_abi, true, null) catch |err| {
if (cur_includes == .any) {
@@ -4695,7 +4782,7 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes:
.os_tag = .windows,
.abi = .gnu,
};
- const target = try std.zig.system.resolveTargetQuery(target_query);
+ const target = resolveTargetQueryOrFatal(target_query);
const is_native_abi = target_query.isNativeAbi();
const detected_libc = try Compilation.detectLibCIncludeDirs(arena, zig_lib_dir, target, is_native_abi, true, null);
return .{
@@ -4757,10 +4844,10 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void {
}
}
- const target_query = try parseTargetQueryOrReportFatalError(gpa, .{
+ const target_query = parseTargetQueryOrReportFatalError(gpa, .{
.arch_os_abi = target_arch_os_abi,
});
- const target = try std.zig.system.resolveTargetQuery(target_query);
+ const target = resolveTargetQueryOrFatal(target_query);
if (print_includes) {
var arena_state = std.heap.ArenaAllocator.init(gpa);
@@ -5024,7 +5111,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
if (!build_options.enable_logging) {
warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{});
} else {
- try log_scopes.append(gpa, args[i]);
+ try log_scopes.append(arena, args[i]);
}
continue;
} else if (mem.eql(u8, arg, "--debug-compile-errors")) {
@@ -5115,7 +5202,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
gimmeMoreOfThoseSweetSweetFileDescriptors();
const target_query: std.Target.Query = .{};
- const target = try std.zig.system.resolveTargetQuery(target_query);
+ const target = resolveTargetQueryOrFatal(target_query);
const exe_basename = try std.zig.binNameAlloc(arena, .{
.root_name = "build",
@@ -5130,29 +5217,80 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
try thread_pool.init(.{ .allocator = gpa });
defer thread_pool.deinit();
- var main_mod: Package.Module = if (override_build_runner) |build_runner_path|
- .{
+ const main_mod_paths: Package.Module.CreateOptions.Paths = if (override_build_runner) |runner| .{
+ .root = .{
+ .root_dir = Cache.Directory.cwd(),
+ .sub_path = fs.path.dirname(runner) orelse "",
+ },
+ .root_src_path = fs.path.basename(runner),
+ } else .{
+ .root = .{ .root_dir = zig_lib_directory },
+ .root_src_path = "build_runner.zig",
+ };
+
+ const config = try Compilation.Config.resolve(.{
+ .output_mode = .Exe,
+ .resolved_target = .{
+ .result = target,
+ .is_native_os = true,
+ .is_native_abi = true,
+ },
+ .have_zcu = true,
+ .emit_bin = true,
+ .is_test = false,
+ });
+
+ const root_mod = try Package.Module.create(arena, .{
+ .global_cache_directory = global_cache_directory,
+ .paths = main_mod_paths,
+ .fully_qualified_name = "root",
+ .cc_argv = &.{},
+ .inherited = .{},
+ .global = config,
+ .parent = null,
+ .builtin_mod = null,
+ });
+
+ const builtin_mod = root_mod.getBuiltinDependency();
+ const std_mod = try Package.Module.create(arena, .{
+ .global_cache_directory = global_cache_directory,
+ .paths = .{
.root = .{
- .root_dir = Cache.Directory.cwd(),
- .sub_path = fs.path.dirname(build_runner_path) orelse "",
+ .root_dir = zig_lib_directory,
+ .sub_path = "std",
},
- .root_src_path = fs.path.basename(build_runner_path),
- .fully_qualified_name = "root",
- }
- else
- .{
- .root = .{ .root_dir = zig_lib_directory },
- .root_src_path = "build_runner.zig",
- .fully_qualified_name = "root",
- };
+ .root_src_path = "std.zig",
+ },
+ .fully_qualified_name = "std",
+ .cc_argv = &.{},
+ .inherited = .{},
+ .global = config,
+ .parent = root_mod,
+ .builtin_mod = builtin_mod,
+ });
- var build_mod: Package.Module = .{
- .root = .{ .root_dir = build_root.directory },
- .root_src_path = build_root.build_zig_basename,
+ const build_mod = try Package.Module.create(arena, .{
+ .global_cache_directory = global_cache_directory,
+ .paths = .{
+ .root = .{ .root_dir = build_root.directory },
+ .root_src_path = build_root.build_zig_basename,
+ },
.fully_qualified_name = "root.@build",
- };
+ .cc_argv = &.{},
+ .inherited = .{},
+ .global = config,
+ .parent = root_mod,
+ .builtin_mod = builtin_mod,
+ });
if (build_options.only_core_functionality) {
- try createEmptyDependenciesModule(arena, &main_mod, local_cache_directory);
+ try createEmptyDependenciesModule(
+ arena,
+ root_mod,
+ global_cache_directory,
+ local_cache_directory,
+ builtin_mod,
+ config,
+ );
} else {
var http_client: std.http.Client = .{ .allocator = gpa };
defer http_client.deinit();
@@ -5196,7 +5334,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
.has_build_zig = true,
.oom_flag = false,
- .module = &build_mod,
+ .module = build_mod,
};
job_queue.all_fetches.appendAssumeCapacity(&fetch);
@@ -5225,8 +5363,11 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
const deps_mod = try createDependenciesModule(
arena,
source_buf.items,
- &main_mod,
+ root_mod,
+ global_cache_directory,
local_cache_directory,
+ builtin_mod,
+ config,
);
{
@@ -5242,13 +5383,21 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
if (!f.has_build_zig)
continue;
const m = try Package.Module.create(arena, .{
- .root = try f.package_root.clone(arena),
- .root_src_path = Package.build_zig_basename,
+ .global_cache_directory = global_cache_directory,
+ .paths = .{
+ .root = try f.package_root.clone(arena),
+ .root_src_path = Package.build_zig_basename,
+ },
.fully_qualified_name = try std.fmt.allocPrint(
arena,
"root.@dependencies.{s}",
.{&hash},
),
+ .cc_argv = &.{},
+ .inherited = .{},
+ .global = config,
+ .parent = root_mod,
+ .builtin_mod = builtin_mod,
});
const hash_cloned = try arena.dupe(u8, &hash);
deps_mod.deps.putAssumeCapacityNoClobber(hash_cloned, m);
@@ -5276,21 +5425,19 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
}
}
- try main_mod.deps.put(arena, "@build", &build_mod);
+ try root_mod.deps.put(arena, "@build", build_mod);
const comp = Compilation.create(gpa, .{
.zig_lib_directory = zig_lib_directory,
.local_cache_directory = local_cache_directory,
.global_cache_directory = global_cache_directory,
.root_name = "build",
- .target = target,
- .is_native_os = target_query.isNativeOs(),
- .is_native_abi = target_query.isNativeAbi(),
- .output_mode = .Exe,
- .main_mod = &main_mod,
+ .config = config,
+ .root_mod = root_mod,
+ .main_mod = build_mod,
+ .std_mod = std_mod,
.emit_bin = emit_bin,
.emit_h = null,
- .optimize_mode = .Debug,
.self_exe_path = self_exe_path,
.thread_pool = &thread_pool,
.verbose_cc = verbose_cc,
@@ -5514,7 +5661,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
.root_decl = .none,
};
- file.mod = try Package.Module.create(arena, .{
+ file.mod = try Package.Module.createLimited(arena, .{
.root = Package.Path.cwd(),
.root_src_path = file.sub_file_path,
.fully_qualified_name = "root",
@@ -5724,7 +5871,7 @@ fn fmtPathFile(
.root_decl = .none,
};
- file.mod = try Package.Module.create(fmt.arena, .{
+ file.mod = try Package.Module.createLimited(fmt.arena, .{
.root = Package.Path.cwd(),
.root_src_path = file.sub_file_path,
.fully_qualified_name = "root",
@@ -5804,15 +5951,13 @@ pub fn putAstErrorsIntoBundle(
.tree = tree,
.tree_loaded = true,
.zir = undefined,
- .mod = undefined,
+ .mod = try Package.Module.createLimited(gpa, .{
+ .root = Package.Path.cwd(),
+ .root_src_path = path,
+ .fully_qualified_name = "root",
+ }),
.root_decl = .none,
};
-
- file.mod = try Package.Module.create(gpa, .{
- .root = Package.Path.cwd(),
- .root_src_path = file.sub_file_path,
- .fully_qualified_name = "root",
- });
defer gpa.destroy(file.mod);
file.zir = try AstGen.generate(gpa, file.tree);
@@ -6373,7 +6518,7 @@ pub fn cmdAstCheck(
file.stat.size = source.len;
}
- file.mod = try Package.Module.create(arena, .{
+ file.mod = try Package.Module.createLimited(arena, .{
.root = Package.Path.cwd(),
.root_src_path = file.sub_file_path,
.fully_qualified_name = "root",
@@ -6546,7 +6691,7 @@ pub fn cmdChangelist(
.root_decl = .none,
};
- file.mod = try Package.Module.create(arena, .{
+ file.mod = try Package.Module.createLimited(arena, .{
.root = Package.Path.cwd(),
.root_src_path = file.sub_file_path,
.fully_qualified_name = "root",
@@ -6669,7 +6814,7 @@ fn warnAboutForeignBinaries(
link_libc: bool,
) !void {
const host_query: std.Target.Query = .{};
- const host_target = try std.zig.system.resolveTargetQuery(host_query);
+ const host_target = resolveTargetQueryOrFatal(host_query);
switch (std.zig.system.getExternalExecutor(host_target, target, .{ .link_libc = link_libc })) {
.native => return,
@@ -6809,18 +6954,22 @@ fn parseSubSystem(next_arg: []const u8) !std.Target.SubSystem {
/// Silently ignore superfluous search dirs.
/// Warn when a dir is added to multiple searchlists.
const ClangSearchSanitizer = struct {
- argv: *std.ArrayList([]const u8),
- map: std.StringHashMap(Membership),
+ map: std.StringHashMapUnmanaged(Membership) = .{},
- fn init(gpa: Allocator, argv: *std.ArrayList([]const u8)) @This() {
- return .{
- .argv = argv,
- .map = std.StringHashMap(Membership).init(gpa),
- };
+ fn reset(self: *@This()) void {
+ self.map.clearRetainingCapacity();
}
- fn addIncludePath(self: *@This(), group: Group, arg: []const u8, dir: []const u8, joined: bool) !void {
- const gopr = try self.map.getOrPut(dir);
+ fn addIncludePath(
+ self: *@This(),
+ ally: Allocator,
+ argv: *std.ArrayListUnmanaged([]const u8),
+ group: Group,
+ arg: []const u8,
+ dir: []const u8,
+ joined: bool,
+ ) !void {
+ const gopr = try self.map.getOrPut(ally, dir);
const m = gopr.value_ptr;
if (!gopr.found_existing) {
// init empty membership
@@ -6867,8 +7016,9 @@ const ClangSearchSanitizer = struct {
if (m.iwithsysroot) warn(wtxt, .{ dir, "iframeworkwithsysroot", "iwithsysroot" });
},
}
- try self.argv.append(arg);
- if (!joined) try self.argv.append(dir);
+ try argv.ensureUnusedCapacity(ally, 2);
+ argv.appendAssumeCapacity(arg);
+ if (!joined) argv.appendAssumeCapacity(dir);
}
const Group = enum { I, isystem, iwithsysroot, idirafter, iframework, iframeworkwithsysroot };
@@ -7244,11 +7394,22 @@ fn cmdFetch(
fn createEmptyDependenciesModule(
arena: Allocator,
main_mod: *Package.Module,
+ global_cache_directory: Cache.Directory,
local_cache_directory: Cache.Directory,
+ builtin_mod: *Package.Module,
+ global_options: Compilation.Config,
) !void {
var source = std.ArrayList(u8).init(arena);
try Package.Fetch.JobQueue.createEmptyDependenciesSource(&source);
- _ = try createDependenciesModule(arena, source.items, main_mod, local_cache_directory);
+ _ = try createDependenciesModule(
+ arena,
+ source.items,
+ main_mod,
+ global_cache_directory,
+ local_cache_directory,
+ builtin_mod,
+ global_options,
+ );
}
/// Creates the dependencies.zig file and corresponding `Package.Module` for the
@@ -7257,7 +7418,10 @@ fn createDependenciesModule(
arena: Allocator,
source: []const u8,
main_mod: *Package.Module,
+ global_cache_directory: Cache.Directory,
local_cache_directory: Cache.Directory,
+ builtin_mod: *Package.Module,
+ global_options: Compilation.Config,
) !*Package.Module {
// Atomically create the file in a directory named after the hash of its contents.
const basename = "dependencies.zig";
@@ -7283,25 +7447,25 @@ fn createDependenciesModule(
);
const deps_mod = try Package.Module.create(arena, .{
- .root = .{
- .root_dir = local_cache_directory,
- .sub_path = o_dir_sub_path,
+ .global_cache_directory = global_cache_directory,
+ .paths = .{
+ .root = .{
+ .root_dir = local_cache_directory,
+ .sub_path = o_dir_sub_path,
+ },
+ .root_src_path = basename,
},
- .root_src_path = basename,
.fully_qualified_name = "root.@dependencies",
+ .parent = main_mod,
+ .builtin_mod = builtin_mod,
+ .cc_argv = &.{},
+ .inherited = .{},
+ .global = global_options,
});
try main_mod.deps.put(arena, "@dependencies", deps_mod);
return deps_mod;
}
-fn defaultWasmEntryName(exec_model: ?std.builtin.WasiExecModel) []const u8 {
- const model = exec_model orelse .command;
- if (model == .reactor) {
- return "_initialize";
- }
- return "_start";
-}
-
const BuildRoot = struct {
directory: Cache.Directory,
build_zig_basename: []const u8,
@@ -7509,3 +7673,18 @@ fn findTemplates(gpa: Allocator, arena: Allocator) Templates {
.buffer = std.ArrayList(u8).init(gpa),
};
}
+
+fn parseOptimizeMode(s: []const u8) std.builtin.OptimizeMode {
+ return std.meta.stringToEnum(std.builtin.OptimizeMode, s) orelse
+ fatal("unrecognized optimization mode: '{s}'", .{s});
+}
+
+fn parseWasiExecModel(s: []const u8) std.builtin.WasiExecModel {
+ return std.meta.stringToEnum(std.builtin.WasiExecModel, s) orelse
+ fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{s});
+}
+
+fn resolveTargetQueryOrFatal(target_query: std.Target.Query) std.Target {
+ return std.zig.system.resolveTargetQuery(target_query) catch |err|
+ fatal("unable to resolve target: {s}", .{@errorName(err)});
+}
src/Module.zig
@@ -59,6 +59,7 @@ root_mod: *Package.Module,
/// Normally, `main_mod` and `root_mod` are the same. The exception is `zig test`, in which
/// `root_mod` is the test runner, and `main_mod` is the user's source file which has the tests.
main_mod: *Package.Module,
+std_mod: *Package.Module,
sema_prog_node: std.Progress.Node = undefined,
/// Used by AstGen worker to load and store ZIR cache.
@@ -3599,7 +3600,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
// TODO: figure out how this works under incremental changes to builtin.zig!
const builtin_type_target_index: InternPool.Index = blk: {
- const std_mod = mod.main_mod.deps.get("std").?;
+ const std_mod = mod.std_mod;
if (decl.getFileScope(mod).mod != std_mod) break :blk .none;
// We're in the std module.
const std_file = (try mod.importPkg(std_mod)).file;
@@ -3924,10 +3925,7 @@ pub fn importFile(
import_string: []const u8,
) !ImportFileResult {
if (std.mem.eql(u8, import_string, "std")) {
- return mod.importPkg(mod.main_mod.deps.get("std").?);
- }
- if (std.mem.eql(u8, import_string, "builtin")) {
- return mod.importPkg(mod.main_mod.deps.get("builtin").?);
+ return mod.importPkg(mod.std_mod);
}
if (std.mem.eql(u8, import_string, "root")) {
return mod.importPkg(mod.root_mod);
src/Sema.zig
@@ -36668,7 +36668,7 @@ fn getBuiltinDecl(sema: *Sema, block: *Block, name: []const u8) CompileError!Int
const mod = sema.mod;
const ip = &mod.intern_pool;
- const std_mod = mod.main_mod.deps.get("std").?;
+ const std_mod = mod.std_mod;
const std_file = (mod.importPkg(std_mod) catch unreachable).file;
const opt_builtin_inst = (try sema.namespaceLookupRef(
block,
src/target.zig
@@ -3,6 +3,8 @@ const Type = @import("type.zig").Type;
const AddressSpace = std.builtin.AddressSpace;
const Alignment = @import("InternPool.zig").Alignment;
+pub const default_stack_protector_buffer_size = 4;
+
pub const ArchOsAbi = struct {
arch: std.Target.Cpu.Arch,
os: std.Target.Os.Tag,
@@ -204,11 +206,18 @@ pub fn supports_fpic(target: std.Target) bool {
return target.os.tag != .windows and target.os.tag != .uefi;
}
-pub fn isSingleThreaded(target: std.Target) bool {
+pub fn alwaysSingleThreaded(target: std.Target) bool {
_ = target;
return false;
}
+pub fn defaultSingleThreaded(target: std.Target) bool {
+ return switch (target.cpu.arch) {
+ .wasm32, .wasm64 => true,
+ else => false,
+ };
+}
+
/// Valgrind supports more, but Zig does not support them yet.
pub fn hasValgrindSupport(target: std.Target) bool {
switch (target.cpu.arch) {
@@ -375,12 +384,17 @@ pub fn classifyCompilerRtLibName(target: std.Target, name: []const u8) CompilerR
}
pub fn hasDebugInfo(target: std.Target) bool {
- if (target.cpu.arch.isNvptx()) {
- // TODO: not sure how to test "ptx >= 7.5" with featureset
- return std.Target.nvptx.featureSetHas(target.cpu.features, .ptx75);
- }
-
- return true;
+ return switch (target.cpu.arch) {
+ .nvptx, .nvptx64 => std.Target.nvptx.featureSetHas(target.cpu.features, .ptx75) or
+ std.Target.nvptx.featureSetHas(target.cpu.features, .ptx76) or
+ std.Target.nvptx.featureSetHas(target.cpu.features, .ptx77) or
+ std.Target.nvptx.featureSetHas(target.cpu.features, .ptx78) or
+ std.Target.nvptx.featureSetHas(target.cpu.features, .ptx80) or
+ std.Target.nvptx.featureSetHas(target.cpu.features, .ptx81),
+ .wasm32, .wasm64 => false,
+ .bpfel, .bpfeb => false,
+ else => true,
+ };
}
pub fn defaultCompilerRtOptimizeMode(target: std.Target) std.builtin.OptimizeMode {
@@ -619,3 +633,36 @@ pub fn fnCallConvAllowsZigTypes(target: std.Target, cc: std.builtin.CallingConve
else => false,
};
}
+
+pub fn zigBackend(target: std.Target, use_llvm: bool) std.builtin.CompilerBackend {
+ if (use_llvm) return .stage2_llvm;
+ if (target.ofmt == .c) return .stage2_c;
+ return switch (target.cpu.arch) {
+ .wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm,
+ .arm, .armeb, .thumb, .thumbeb => .stage2_arm,
+ .x86_64 => .stage2_x86_64,
+ .x86 => .stage2_x86,
+ .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64,
+ .riscv64 => .stage2_riscv64,
+ .sparc64 => .stage2_sparc64,
+ .spirv64 => .stage2_spirv64,
+ else => .other,
+ };
+}
+
+pub fn defaultEntrySymbolName(
+ target: std.Target,
+ /// May be `undefined` when `target` is not WASI.
+ wasi_exec_model: std.builtin.WasiExecModel,
+) ?[]const u8 {
+ return switch (target.ofmt) {
+ .coff => "wWinMainCRTStartup",
+ .macho => "_main",
+ .elf, .plan9 => "_start",
+ .wasm => switch (wasi_exec_model) {
+ .reactor => "_initialize",
+ .command => "_start",
+ },
+ else => null,
+ };
+}
CMakeLists.txt
@@ -521,6 +521,7 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/src/Air.zig"
"${CMAKE_SOURCE_DIR}/src/AstGen.zig"
"${CMAKE_SOURCE_DIR}/src/Compilation.zig"
+ "${CMAKE_SOURCE_DIR}/src/Compilation/Config.zig"
"${CMAKE_SOURCE_DIR}/src/Liveness.zig"
"${CMAKE_SOURCE_DIR}/src/Module.zig"
"${CMAKE_SOURCE_DIR}/src/Package.zig"