mirror of
https://github.com/ziglang/zig.git
synced 2025-12-25 15:43:06 +00:00
These options are only supposed to be provided to the initialization functions, resolved, and then computed values stored in the appropriate place (base struct or the object-format-specific structs). Many more to go...
395 lines
14 KiB
Zig
395 lines
14 KiB
Zig
//! 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;
|
|
if (!target_util.hasLlvmSupport(target, target.ofmt))
|
|
return error.LlvmLacksTargetSupport;
|
|
|
|
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 Zig does not support the target, then we can't use it.
|
|
if (target_util.zigBackend(target, false) == .other) {
|
|
if (options.use_llvm == false) return error.ZigLacksTargetSupport;
|
|
break :b true;
|
|
}
|
|
|
|
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 (options.emit_bin) {
|
|
if (!use_lib_llvm and use_llvm) {
|
|
// Explicit request to use LLVM to produce an object file, but without
|
|
// using LLVM libraries. Impossible.
|
|
return error.EmittingBinaryRequiresLlvmLibrary;
|
|
}
|
|
|
|
if (target_util.zigBackend(target, use_llvm) == .other) {
|
|
// There is no compiler backend available for this target.
|
|
return error.ZigLacksTargetSupport;
|
|
}
|
|
}
|
|
|
|
// Make a decision on whether to use LLD or our own linker.
|
|
const use_lld = b: {
|
|
if (!target_util.hasLldSupport(target.ofmt)) {
|
|
if (options.use_lld == true) return error.LldIncompatibleObjectFormat;
|
|
break :b false;
|
|
}
|
|
|
|
if (!build_options.have_llvm) {
|
|
if (options.use_lld == true) return error.LldUnavailable;
|
|
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");
|