mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
Introduce the concept of "target query" and "resolved target". A target query is what the user specifies, with some things left to default. A resolved target has the default things discovered and populated. In the future, std.zig.CrossTarget will be rename to std.Target.Query. Introduces `std.Build.resolveTargetQuery` to get from one to the other. The concept of `main_mod_path` is gone, no longer supported. You have to put the root source file at the module root now. * remove deprecated API * update build.zig for the breaking API changes in this branch * move std.Build.Step.Compile.BuildId to std.zig.BuildId * add more options to std.Build.ExecutableOptions, std.Build.ObjectOptions, std.Build.SharedLibraryOptions, std.Build.StaticLibraryOptions, and std.Build.TestOptions. * remove `std.Build.constructCMacro`. There is no use for this API. * deprecate `std.Build.Step.Compile.defineCMacro`. Instead, `std.Build.Module.addCMacro` is provided. - remove `std.Build.Step.Compile.defineCMacroRaw`. * deprecate `std.Build.Step.Compile.linkFrameworkNeeded` - use `std.Build.Module.linkFramework` * deprecate `std.Build.Step.Compile.linkFrameworkWeak` - use `std.Build.Module.linkFramework` * move more logic into `std.Build.Module` * allow `target` and `optimize` to be `null` when creating a Module. Along with other fields, those unspecified options will be inherited from parent `Module` when inserted into an import table. * the `target` field of `addExecutable` is now required. pass `b.host` to get the host target.
855 lines
30 KiB
Zig
855 lines
30 KiB
Zig
//! Contains all the same data as `Target`, additionally introducing the concept of "the native target".
|
|
//! The purpose of this abstraction is to provide meaningful and unsurprising defaults.
|
|
//! This struct does reference any resources and it is copyable.
|
|
|
|
const CrossTarget = @This();
|
|
const std = @import("../std.zig");
|
|
const builtin = @import("builtin");
|
|
const assert = std.debug.assert;
|
|
const Target = std.Target;
|
|
const mem = std.mem;
|
|
|
|
/// `null` means native.
|
|
cpu_arch: ?Target.Cpu.Arch = null,
|
|
|
|
cpu_model: CpuModel = CpuModel.determined_by_cpu_arch,
|
|
|
|
/// Sparse set of CPU features to add to the set from `cpu_model`.
|
|
cpu_features_add: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
|
|
|
|
/// Sparse set of CPU features to remove from the set from `cpu_model`.
|
|
cpu_features_sub: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
|
|
|
|
/// `null` means native.
|
|
os_tag: ?Target.Os.Tag = null,
|
|
|
|
/// `null` means the default version range for `os_tag`. If `os_tag` is `null` (native)
|
|
/// then `null` for this field means native.
|
|
os_version_min: ?OsVersion = null,
|
|
|
|
/// When cross compiling, `null` means default (latest known OS version).
|
|
/// When `os_tag` is native, `null` means equal to the native OS version.
|
|
os_version_max: ?OsVersion = null,
|
|
|
|
/// `null` means default when cross compiling, or native when os_tag is native.
|
|
/// If `isGnuLibC()` is `false`, this must be `null` and is ignored.
|
|
glibc_version: ?SemanticVersion = null,
|
|
|
|
/// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI.
|
|
abi: ?Target.Abi = null,
|
|
|
|
/// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path
|
|
/// based on the `os_tag`.
|
|
dynamic_linker: DynamicLinker = DynamicLinker{},
|
|
|
|
/// `null` means default for the cpu/arch/os combo.
|
|
ofmt: ?Target.ObjectFormat = null,
|
|
|
|
pub const CpuModel = union(enum) {
|
|
/// Always native
|
|
native,
|
|
|
|
/// Always baseline
|
|
baseline,
|
|
|
|
/// If CPU Architecture is native, then the CPU model will be native. Otherwise,
|
|
/// it will be baseline.
|
|
determined_by_cpu_arch,
|
|
|
|
explicit: *const Target.Cpu.Model,
|
|
};
|
|
|
|
pub const OsVersion = union(enum) {
|
|
none: void,
|
|
semver: SemanticVersion,
|
|
windows: Target.Os.WindowsVersion,
|
|
};
|
|
|
|
pub const SemanticVersion = std.SemanticVersion;
|
|
|
|
pub const DynamicLinker = Target.DynamicLinker;
|
|
|
|
pub fn fromTarget(target: Target) CrossTarget {
|
|
var result: CrossTarget = .{
|
|
.cpu_arch = target.cpu.arch,
|
|
.cpu_model = .{ .explicit = target.cpu.model },
|
|
.os_tag = target.os.tag,
|
|
.os_version_min = undefined,
|
|
.os_version_max = undefined,
|
|
.abi = target.abi,
|
|
.glibc_version = if (target.isGnuLibC())
|
|
target.os.version_range.linux.glibc
|
|
else
|
|
null,
|
|
};
|
|
result.updateOsVersionRange(target.os);
|
|
|
|
const all_features = target.cpu.arch.allFeaturesList();
|
|
var cpu_model_set = target.cpu.model.features;
|
|
cpu_model_set.populateDependencies(all_features);
|
|
{
|
|
// The "add" set is the full set with the CPU Model set removed.
|
|
const add_set = &result.cpu_features_add;
|
|
add_set.* = target.cpu.features;
|
|
add_set.removeFeatureSet(cpu_model_set);
|
|
}
|
|
{
|
|
// The "sub" set is the features that are on in CPU Model set and off in the full set.
|
|
const sub_set = &result.cpu_features_sub;
|
|
sub_set.* = cpu_model_set;
|
|
sub_set.removeFeatureSet(target.cpu.features);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
fn updateOsVersionRange(self: *CrossTarget, os: Target.Os) void {
|
|
switch (os.tag) {
|
|
.freestanding,
|
|
.ananas,
|
|
.cloudabi,
|
|
.fuchsia,
|
|
.kfreebsd,
|
|
.lv2,
|
|
.solaris,
|
|
.illumos,
|
|
.zos,
|
|
.haiku,
|
|
.minix,
|
|
.rtems,
|
|
.nacl,
|
|
.aix,
|
|
.cuda,
|
|
.nvcl,
|
|
.amdhsa,
|
|
.ps4,
|
|
.ps5,
|
|
.elfiamcu,
|
|
.mesa3d,
|
|
.contiki,
|
|
.amdpal,
|
|
.hermit,
|
|
.hurd,
|
|
.wasi,
|
|
.emscripten,
|
|
.driverkit,
|
|
.shadermodel,
|
|
.liteos,
|
|
.uefi,
|
|
.opencl,
|
|
.glsl450,
|
|
.vulkan,
|
|
.plan9,
|
|
.other,
|
|
=> {
|
|
self.os_version_min = .{ .none = {} };
|
|
self.os_version_max = .{ .none = {} };
|
|
},
|
|
|
|
.freebsd,
|
|
.macos,
|
|
.ios,
|
|
.tvos,
|
|
.watchos,
|
|
.netbsd,
|
|
.openbsd,
|
|
.dragonfly,
|
|
=> {
|
|
self.os_version_min = .{ .semver = os.version_range.semver.min };
|
|
self.os_version_max = .{ .semver = os.version_range.semver.max };
|
|
},
|
|
|
|
.linux => {
|
|
self.os_version_min = .{ .semver = os.version_range.linux.range.min };
|
|
self.os_version_max = .{ .semver = os.version_range.linux.range.max };
|
|
},
|
|
|
|
.windows => {
|
|
self.os_version_min = .{ .windows = os.version_range.windows.min };
|
|
self.os_version_max = .{ .windows = os.version_range.windows.max };
|
|
},
|
|
}
|
|
}
|
|
|
|
/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
|
|
pub fn toTarget(self: CrossTarget) Target {
|
|
return .{
|
|
.cpu = self.getCpu(),
|
|
.os = self.getOs(),
|
|
.abi = self.getAbi(),
|
|
.ofmt = self.getObjectFormat(),
|
|
};
|
|
}
|
|
|
|
pub const ParseOptions = struct {
|
|
/// This is sometimes called a "triple". It looks roughly like this:
|
|
/// riscv64-linux-musl
|
|
/// The fields are, respectively:
|
|
/// * CPU Architecture
|
|
/// * Operating System (and optional version range)
|
|
/// * C ABI (optional, with optional glibc version)
|
|
/// The string "native" can be used for CPU architecture as well as Operating System.
|
|
/// If the CPU Architecture is specified as "native", then the Operating System and C ABI may be omitted.
|
|
arch_os_abi: []const u8 = "native",
|
|
|
|
/// Looks like "name+a+b-c-d+e", where "name" is a CPU Model name, "a", "b", and "e"
|
|
/// are examples of CPU features to add to the set, and "c" and "d" are examples of CPU features
|
|
/// to remove from the set.
|
|
/// The following special strings are recognized for CPU Model name:
|
|
/// * "baseline" - The "default" set of CPU features for cross-compiling. A conservative set
|
|
/// of features that is expected to be supported on most available hardware.
|
|
/// * "native" - The native CPU model is to be detected when compiling.
|
|
/// If this field is not provided (`null`), then the value will depend on the
|
|
/// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline".
|
|
cpu_features: ?[]const u8 = null,
|
|
|
|
/// Absolute path to dynamic linker, to override the default, which is either a natively
|
|
/// detected path, or a standard path.
|
|
dynamic_linker: ?[]const u8 = null,
|
|
|
|
object_format: ?[]const u8 = null,
|
|
|
|
/// If this is provided, the function will populate some information about parsing failures,
|
|
/// so that user-friendly error messages can be delivered.
|
|
diagnostics: ?*Diagnostics = null,
|
|
|
|
pub const Diagnostics = struct {
|
|
/// If the architecture was determined, this will be populated.
|
|
arch: ?Target.Cpu.Arch = null,
|
|
|
|
/// If the OS name was determined, this will be populated.
|
|
os_name: ?[]const u8 = null,
|
|
|
|
/// If the OS tag was determined, this will be populated.
|
|
os_tag: ?Target.Os.Tag = null,
|
|
|
|
/// If the ABI was determined, this will be populated.
|
|
abi: ?Target.Abi = null,
|
|
|
|
/// If the CPU name was determined, this will be populated.
|
|
cpu_name: ?[]const u8 = null,
|
|
|
|
/// If error.UnknownCpuFeature is returned, this will be populated.
|
|
unknown_feature_name: ?[]const u8 = null,
|
|
};
|
|
};
|
|
|
|
pub fn parse(args: ParseOptions) !CrossTarget {
|
|
var dummy_diags: ParseOptions.Diagnostics = undefined;
|
|
const diags = args.diagnostics orelse &dummy_diags;
|
|
|
|
var result: CrossTarget = .{
|
|
.dynamic_linker = DynamicLinker.init(args.dynamic_linker),
|
|
};
|
|
|
|
var it = mem.splitScalar(u8, args.arch_os_abi, '-');
|
|
const arch_name = it.first();
|
|
const arch_is_native = mem.eql(u8, arch_name, "native");
|
|
if (!arch_is_native) {
|
|
result.cpu_arch = std.meta.stringToEnum(Target.Cpu.Arch, arch_name) orelse
|
|
return error.UnknownArchitecture;
|
|
}
|
|
const arch = result.getCpuArch();
|
|
diags.arch = arch;
|
|
|
|
if (it.next()) |os_text| {
|
|
try parseOs(&result, diags, os_text);
|
|
} else if (!arch_is_native) {
|
|
return error.MissingOperatingSystem;
|
|
}
|
|
|
|
const opt_abi_text = it.next();
|
|
if (opt_abi_text) |abi_text| {
|
|
var abi_it = mem.splitScalar(u8, abi_text, '.');
|
|
const abi = std.meta.stringToEnum(Target.Abi, abi_it.first()) orelse
|
|
return error.UnknownApplicationBinaryInterface;
|
|
result.abi = abi;
|
|
diags.abi = abi;
|
|
|
|
const abi_ver_text = abi_it.rest();
|
|
if (abi_it.next() != null) {
|
|
if (result.isGnuLibC()) {
|
|
result.glibc_version = parseVersion(abi_ver_text) catch |err| switch (err) {
|
|
error.Overflow => return error.InvalidAbiVersion,
|
|
error.InvalidVersion => return error.InvalidAbiVersion,
|
|
};
|
|
} else {
|
|
return error.InvalidAbiVersion;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (it.next() != null) return error.UnexpectedExtraField;
|
|
|
|
if (args.cpu_features) |cpu_features| {
|
|
const all_features = arch.allFeaturesList();
|
|
var index: usize = 0;
|
|
while (index < cpu_features.len and
|
|
cpu_features[index] != '+' and
|
|
cpu_features[index] != '-')
|
|
{
|
|
index += 1;
|
|
}
|
|
const cpu_name = cpu_features[0..index];
|
|
diags.cpu_name = cpu_name;
|
|
|
|
const add_set = &result.cpu_features_add;
|
|
const sub_set = &result.cpu_features_sub;
|
|
if (mem.eql(u8, cpu_name, "native")) {
|
|
result.cpu_model = .native;
|
|
} else if (mem.eql(u8, cpu_name, "baseline")) {
|
|
result.cpu_model = .baseline;
|
|
} else {
|
|
result.cpu_model = .{ .explicit = try arch.parseCpuModel(cpu_name) };
|
|
}
|
|
|
|
while (index < cpu_features.len) {
|
|
const op = cpu_features[index];
|
|
const set = switch (op) {
|
|
'+' => add_set,
|
|
'-' => sub_set,
|
|
else => unreachable,
|
|
};
|
|
index += 1;
|
|
const start = index;
|
|
while (index < cpu_features.len and
|
|
cpu_features[index] != '+' and
|
|
cpu_features[index] != '-')
|
|
{
|
|
index += 1;
|
|
}
|
|
const feature_name = cpu_features[start..index];
|
|
for (all_features, 0..) |feature, feat_index_usize| {
|
|
const feat_index = @as(Target.Cpu.Feature.Set.Index, @intCast(feat_index_usize));
|
|
if (mem.eql(u8, feature_name, feature.name)) {
|
|
set.addFeature(feat_index);
|
|
break;
|
|
}
|
|
} else {
|
|
diags.unknown_feature_name = feature_name;
|
|
return error.UnknownCpuFeature;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (args.object_format) |ofmt_name| {
|
|
result.ofmt = std.meta.stringToEnum(Target.ObjectFormat, ofmt_name) orelse
|
|
return error.UnknownObjectFormat;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// Similar to `parse` except instead of fully parsing, it only determines the CPU
|
|
/// architecture and returns it if it can be determined, and returns `null` otherwise.
|
|
/// This is intended to be used if the API user of CrossTarget needs to learn the
|
|
/// target CPU architecture in order to fully populate `ParseOptions`.
|
|
pub fn parseCpuArch(args: ParseOptions) ?Target.Cpu.Arch {
|
|
var it = mem.splitScalar(u8, args.arch_os_abi, '-');
|
|
const arch_name = it.first();
|
|
const arch_is_native = mem.eql(u8, arch_name, "native");
|
|
if (arch_is_native) {
|
|
return builtin.cpu.arch;
|
|
} else {
|
|
return std.meta.stringToEnum(Target.Cpu.Arch, arch_name);
|
|
}
|
|
}
|
|
|
|
/// Similar to `SemanticVersion.parse`, but with following changes:
|
|
/// * Leading zeroes are allowed.
|
|
/// * Supports only 2 or 3 version components (major, minor, [patch]). If 3-rd component is omitted, it will be 0.
|
|
pub fn parseVersion(ver: []const u8) error{ InvalidVersion, Overflow }!SemanticVersion {
|
|
const parseVersionComponentFn = (struct {
|
|
fn parseVersionComponentInner(component: []const u8) error{ InvalidVersion, Overflow }!usize {
|
|
return std.fmt.parseUnsigned(usize, component, 10) catch |err| switch (err) {
|
|
error.InvalidCharacter => return error.InvalidVersion,
|
|
error.Overflow => return error.Overflow,
|
|
};
|
|
}
|
|
}).parseVersionComponentInner;
|
|
var version_components = mem.splitScalar(u8, ver, '.');
|
|
const major = version_components.first();
|
|
const minor = version_components.next() orelse return error.InvalidVersion;
|
|
const patch = version_components.next() orelse "0";
|
|
if (version_components.next() != null) return error.InvalidVersion;
|
|
return .{
|
|
.major = try parseVersionComponentFn(major),
|
|
.minor = try parseVersionComponentFn(minor),
|
|
.patch = try parseVersionComponentFn(patch),
|
|
};
|
|
}
|
|
|
|
test parseVersion {
|
|
try std.testing.expectError(error.InvalidVersion, parseVersion("1"));
|
|
try std.testing.expectEqual(SemanticVersion{ .major = 1, .minor = 2, .patch = 0 }, try parseVersion("1.2"));
|
|
try std.testing.expectEqual(SemanticVersion{ .major = 1, .minor = 2, .patch = 3 }, try parseVersion("1.2.3"));
|
|
try std.testing.expectError(error.InvalidVersion, parseVersion("1.2.3.4"));
|
|
}
|
|
|
|
/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
|
|
pub fn getCpu(self: CrossTarget) Target.Cpu {
|
|
switch (self.cpu_model) {
|
|
.native => {
|
|
// This works when doing `zig build` because Zig generates a build executable using
|
|
// native CPU model & features. However this will not be accurate otherwise, and
|
|
// will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
|
|
return builtin.cpu;
|
|
},
|
|
.baseline => {
|
|
var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch());
|
|
self.updateCpuFeatures(&adjusted_baseline.features);
|
|
return adjusted_baseline;
|
|
},
|
|
.determined_by_cpu_arch => if (self.cpu_arch == null) {
|
|
// This works when doing `zig build` because Zig generates a build executable using
|
|
// native CPU model & features. However this will not be accurate otherwise, and
|
|
// will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
|
|
return builtin.cpu;
|
|
} else {
|
|
var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch());
|
|
self.updateCpuFeatures(&adjusted_baseline.features);
|
|
return adjusted_baseline;
|
|
},
|
|
.explicit => |model| {
|
|
var adjusted_model = model.toCpu(self.getCpuArch());
|
|
self.updateCpuFeatures(&adjusted_model.features);
|
|
return adjusted_model;
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn getCpuArch(self: CrossTarget) Target.Cpu.Arch {
|
|
return self.cpu_arch orelse builtin.cpu.arch;
|
|
}
|
|
|
|
pub fn getCpuModel(self: CrossTarget) *const Target.Cpu.Model {
|
|
return switch (self.cpu_model) {
|
|
.explicit => |cpu_model| cpu_model,
|
|
else => self.getCpu().model,
|
|
};
|
|
}
|
|
|
|
pub fn getCpuFeatures(self: CrossTarget) Target.Cpu.Feature.Set {
|
|
return self.getCpu().features;
|
|
}
|
|
|
|
/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
|
|
pub fn getOs(self: CrossTarget) Target.Os {
|
|
// `builtin.os` works when doing `zig build` because Zig generates a build executable using
|
|
// native OS version range. However this will not be accurate otherwise, and
|
|
// will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
|
|
var adjusted_os = if (self.os_tag) |os_tag| os_tag.defaultVersionRange(self.getCpuArch()) else builtin.os;
|
|
|
|
if (self.os_version_min) |min| switch (min) {
|
|
.none => {},
|
|
.semver => |semver| switch (self.getOsTag()) {
|
|
.linux => adjusted_os.version_range.linux.range.min = semver,
|
|
else => adjusted_os.version_range.semver.min = semver,
|
|
},
|
|
.windows => |win_ver| adjusted_os.version_range.windows.min = win_ver,
|
|
};
|
|
|
|
if (self.os_version_max) |max| switch (max) {
|
|
.none => {},
|
|
.semver => |semver| switch (self.getOsTag()) {
|
|
.linux => adjusted_os.version_range.linux.range.max = semver,
|
|
else => adjusted_os.version_range.semver.max = semver,
|
|
},
|
|
.windows => |win_ver| adjusted_os.version_range.windows.max = win_ver,
|
|
};
|
|
|
|
if (self.glibc_version) |glibc| {
|
|
assert(self.isGnuLibC());
|
|
adjusted_os.version_range.linux.glibc = glibc;
|
|
}
|
|
|
|
return adjusted_os;
|
|
}
|
|
|
|
pub fn getOsTag(self: CrossTarget) Target.Os.Tag {
|
|
return self.os_tag orelse builtin.os.tag;
|
|
}
|
|
|
|
/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
|
|
pub fn getOsVersionMin(self: CrossTarget) OsVersion {
|
|
if (self.os_version_min) |version_min| return version_min;
|
|
var tmp: CrossTarget = undefined;
|
|
tmp.updateOsVersionRange(self.getOs());
|
|
return tmp.os_version_min.?;
|
|
}
|
|
|
|
/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
|
|
pub fn getOsVersionMax(self: CrossTarget) OsVersion {
|
|
if (self.os_version_max) |version_max| return version_max;
|
|
var tmp: CrossTarget = undefined;
|
|
tmp.updateOsVersionRange(self.getOs());
|
|
return tmp.os_version_max.?;
|
|
}
|
|
|
|
/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
|
|
pub fn getAbi(self: CrossTarget) Target.Abi {
|
|
if (self.abi) |abi| return abi;
|
|
|
|
if (self.os_tag == null) {
|
|
// This works when doing `zig build` because Zig generates a build executable using
|
|
// native CPU model & features. However this will not be accurate otherwise, and
|
|
// will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
|
|
return builtin.abi;
|
|
}
|
|
|
|
return Target.Abi.default(self.getCpuArch(), self.getOs());
|
|
}
|
|
|
|
pub fn isFreeBSD(self: CrossTarget) bool {
|
|
return self.getOsTag() == .freebsd;
|
|
}
|
|
|
|
pub fn isDarwin(self: CrossTarget) bool {
|
|
return self.getOsTag().isDarwin();
|
|
}
|
|
|
|
pub fn isNetBSD(self: CrossTarget) bool {
|
|
return self.getOsTag() == .netbsd;
|
|
}
|
|
|
|
pub fn isOpenBSD(self: CrossTarget) bool {
|
|
return self.getOsTag() == .openbsd;
|
|
}
|
|
|
|
pub fn isUefi(self: CrossTarget) bool {
|
|
return self.getOsTag() == .uefi;
|
|
}
|
|
|
|
pub fn isDragonFlyBSD(self: CrossTarget) bool {
|
|
return self.getOsTag() == .dragonfly;
|
|
}
|
|
|
|
pub fn isLinux(self: CrossTarget) bool {
|
|
return self.getOsTag() == .linux;
|
|
}
|
|
|
|
pub fn isWindows(self: CrossTarget) bool {
|
|
return self.getOsTag() == .windows;
|
|
}
|
|
|
|
pub fn exeFileExt(self: CrossTarget) [:0]const u8 {
|
|
return Target.exeFileExtSimple(self.getCpuArch(), self.getOsTag());
|
|
}
|
|
|
|
pub fn staticLibSuffix(self: CrossTarget) [:0]const u8 {
|
|
return Target.staticLibSuffix_os_abi(self.getOsTag(), self.getAbi());
|
|
}
|
|
|
|
pub fn dynamicLibSuffix(self: CrossTarget) [:0]const u8 {
|
|
return self.getOsTag().dynamicLibSuffix();
|
|
}
|
|
|
|
pub fn libPrefix(self: CrossTarget) [:0]const u8 {
|
|
return Target.libPrefix_os_abi(self.getOsTag(), self.getAbi());
|
|
}
|
|
|
|
pub fn isNativeCpu(self: CrossTarget) bool {
|
|
return self.cpu_arch == null and
|
|
(self.cpu_model == .native or self.cpu_model == .determined_by_cpu_arch) and
|
|
self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty();
|
|
}
|
|
|
|
pub fn isNativeOs(self: CrossTarget) bool {
|
|
return self.os_tag == null and self.os_version_min == null and self.os_version_max == null and
|
|
self.dynamic_linker.get() == null and self.glibc_version == null;
|
|
}
|
|
|
|
pub fn isNativeAbi(self: CrossTarget) bool {
|
|
return self.os_tag == null and self.abi == null;
|
|
}
|
|
|
|
pub fn isNative(self: CrossTarget) bool {
|
|
return self.isNativeCpu() and self.isNativeOs() and self.isNativeAbi();
|
|
}
|
|
|
|
/// Formats a version with the patch component omitted if it is zero,
|
|
/// unlike SemanticVersion.format which formats all its version components regardless.
|
|
fn formatVersion(version: SemanticVersion, writer: anytype) !void {
|
|
if (version.patch == 0) {
|
|
try writer.print("{d}.{d}", .{ version.major, version.minor });
|
|
} else {
|
|
try writer.print("{d}.{d}.{d}", .{ version.major, version.minor, version.patch });
|
|
}
|
|
}
|
|
|
|
pub fn zigTriple(self: CrossTarget, allocator: mem.Allocator) error{OutOfMemory}![]u8 {
|
|
if (self.isNative()) {
|
|
return allocator.dupe(u8, "native");
|
|
}
|
|
|
|
const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native";
|
|
const os_name = if (self.os_tag) |os_tag| @tagName(os_tag) else "native";
|
|
|
|
var result = std.ArrayList(u8).init(allocator);
|
|
defer result.deinit();
|
|
|
|
try result.writer().print("{s}-{s}", .{ arch_name, os_name });
|
|
|
|
// The zig target syntax does not allow specifying a max os version with no min, so
|
|
// if either are present, we need the min.
|
|
if (self.os_version_min != null or self.os_version_max != null) {
|
|
switch (self.getOsVersionMin()) {
|
|
.none => {},
|
|
.semver => |v| {
|
|
try result.writer().writeAll(".");
|
|
try formatVersion(v, result.writer());
|
|
},
|
|
.windows => |v| try result.writer().print("{s}", .{v}),
|
|
}
|
|
}
|
|
if (self.os_version_max) |max| {
|
|
switch (max) {
|
|
.none => {},
|
|
.semver => |v| {
|
|
try result.writer().writeAll("...");
|
|
try formatVersion(v, result.writer());
|
|
},
|
|
.windows => |v| try result.writer().print("..{s}", .{v}),
|
|
}
|
|
}
|
|
|
|
if (self.glibc_version) |v| {
|
|
try result.writer().print("-{s}.", .{@tagName(self.getAbi())});
|
|
try formatVersion(v, result.writer());
|
|
} else if (self.abi) |abi| {
|
|
try result.writer().print("-{s}", .{@tagName(abi)});
|
|
}
|
|
|
|
return result.toOwnedSlice();
|
|
}
|
|
|
|
pub fn allocDescription(self: CrossTarget, allocator: mem.Allocator) ![]u8 {
|
|
// TODO is there anything else worthy of the description that is not
|
|
// already captured in the triple?
|
|
return self.zigTriple(allocator);
|
|
}
|
|
|
|
pub fn linuxTriple(self: CrossTarget, allocator: mem.Allocator) ![]u8 {
|
|
return Target.linuxTripleSimple(allocator, self.getCpuArch(), self.getOsTag(), self.getAbi());
|
|
}
|
|
|
|
pub fn isGnuLibC(self: CrossTarget) bool {
|
|
return Target.isGnuLibC_os_tag_abi(self.getOsTag(), self.getAbi());
|
|
}
|
|
|
|
pub fn setGnuLibCVersion(self: *CrossTarget, major: u32, minor: u32, patch: u32) void {
|
|
assert(self.isGnuLibC());
|
|
self.glibc_version = SemanticVersion{ .major = major, .minor = minor, .patch = patch };
|
|
}
|
|
|
|
pub fn getObjectFormat(self: CrossTarget) Target.ObjectFormat {
|
|
return self.ofmt orelse Target.ObjectFormat.default(self.getOsTag(), self.getCpuArch());
|
|
}
|
|
|
|
pub fn updateCpuFeatures(self: CrossTarget, set: *Target.Cpu.Feature.Set) void {
|
|
set.removeFeatureSet(self.cpu_features_sub);
|
|
set.addFeatureSet(self.cpu_features_add);
|
|
set.populateDependencies(self.getCpuArch().allFeaturesList());
|
|
set.removeFeatureSet(self.cpu_features_sub);
|
|
}
|
|
|
|
fn parseOs(result: *CrossTarget, diags: *ParseOptions.Diagnostics, text: []const u8) !void {
|
|
var it = mem.splitScalar(u8, text, '.');
|
|
const os_name = it.first();
|
|
diags.os_name = os_name;
|
|
const os_is_native = mem.eql(u8, os_name, "native");
|
|
if (!os_is_native) {
|
|
result.os_tag = std.meta.stringToEnum(Target.Os.Tag, os_name) orelse
|
|
return error.UnknownOperatingSystem;
|
|
}
|
|
const tag = result.getOsTag();
|
|
diags.os_tag = tag;
|
|
|
|
const version_text = it.rest();
|
|
if (it.next() == null) return;
|
|
|
|
switch (tag) {
|
|
.freestanding,
|
|
.ananas,
|
|
.cloudabi,
|
|
.fuchsia,
|
|
.kfreebsd,
|
|
.lv2,
|
|
.solaris,
|
|
.illumos,
|
|
.zos,
|
|
.haiku,
|
|
.minix,
|
|
.rtems,
|
|
.nacl,
|
|
.aix,
|
|
.cuda,
|
|
.nvcl,
|
|
.amdhsa,
|
|
.ps4,
|
|
.ps5,
|
|
.elfiamcu,
|
|
.mesa3d,
|
|
.contiki,
|
|
.amdpal,
|
|
.hermit,
|
|
.hurd,
|
|
.wasi,
|
|
.emscripten,
|
|
.uefi,
|
|
.opencl,
|
|
.glsl450,
|
|
.vulkan,
|
|
.plan9,
|
|
.driverkit,
|
|
.shadermodel,
|
|
.liteos,
|
|
.other,
|
|
=> return error.InvalidOperatingSystemVersion,
|
|
|
|
.freebsd,
|
|
.macos,
|
|
.ios,
|
|
.tvos,
|
|
.watchos,
|
|
.netbsd,
|
|
.openbsd,
|
|
.linux,
|
|
.dragonfly,
|
|
=> {
|
|
var range_it = mem.splitSequence(u8, version_text, "...");
|
|
|
|
const min_text = range_it.next().?;
|
|
const min_ver = parseVersion(min_text) catch |err| switch (err) {
|
|
error.Overflow => return error.InvalidOperatingSystemVersion,
|
|
error.InvalidVersion => return error.InvalidOperatingSystemVersion,
|
|
};
|
|
result.os_version_min = .{ .semver = min_ver };
|
|
|
|
const max_text = range_it.next() orelse return;
|
|
const max_ver = parseVersion(max_text) catch |err| switch (err) {
|
|
error.Overflow => return error.InvalidOperatingSystemVersion,
|
|
error.InvalidVersion => return error.InvalidOperatingSystemVersion,
|
|
};
|
|
result.os_version_max = .{ .semver = max_ver };
|
|
},
|
|
|
|
.windows => {
|
|
var range_it = mem.splitSequence(u8, version_text, "...");
|
|
|
|
const min_text = range_it.first();
|
|
const min_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, min_text) orelse
|
|
return error.InvalidOperatingSystemVersion;
|
|
result.os_version_min = .{ .windows = min_ver };
|
|
|
|
const max_text = range_it.next() orelse return;
|
|
const max_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, max_text) orelse
|
|
return error.InvalidOperatingSystemVersion;
|
|
result.os_version_max = .{ .windows = max_ver };
|
|
},
|
|
}
|
|
}
|
|
|
|
test "CrossTarget.parse" {
|
|
if (builtin.target.isGnuLibC()) {
|
|
var cross_target = try CrossTarget.parse(.{});
|
|
cross_target.setGnuLibCVersion(2, 1, 1);
|
|
|
|
const text = try cross_target.zigTriple(std.testing.allocator);
|
|
defer std.testing.allocator.free(text);
|
|
|
|
var buf: [256]u8 = undefined;
|
|
const triple = std.fmt.bufPrint(
|
|
buf[0..],
|
|
"native-native-{s}.2.1.1",
|
|
.{@tagName(builtin.abi)},
|
|
) catch unreachable;
|
|
|
|
try std.testing.expectEqualSlices(u8, triple, text);
|
|
}
|
|
{
|
|
const cross_target = try CrossTarget.parse(.{
|
|
.arch_os_abi = "aarch64-linux",
|
|
.cpu_features = "native",
|
|
});
|
|
|
|
try std.testing.expect(cross_target.cpu_arch.? == .aarch64);
|
|
try std.testing.expect(cross_target.cpu_model == .native);
|
|
}
|
|
{
|
|
const cross_target = try CrossTarget.parse(.{ .arch_os_abi = "native" });
|
|
|
|
try std.testing.expect(cross_target.cpu_arch == null);
|
|
try std.testing.expect(cross_target.isNative());
|
|
|
|
const text = try cross_target.zigTriple(std.testing.allocator);
|
|
defer std.testing.allocator.free(text);
|
|
try std.testing.expectEqualSlices(u8, "native", text);
|
|
}
|
|
{
|
|
const cross_target = try CrossTarget.parse(.{
|
|
.arch_os_abi = "x86_64-linux-gnu",
|
|
.cpu_features = "x86_64-sse-sse2-avx-cx8",
|
|
});
|
|
const target = cross_target.toTarget();
|
|
|
|
try std.testing.expect(target.os.tag == .linux);
|
|
try std.testing.expect(target.abi == .gnu);
|
|
try std.testing.expect(target.cpu.arch == .x86_64);
|
|
try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse));
|
|
try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .avx));
|
|
try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .cx8));
|
|
try std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .cmov));
|
|
try std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr));
|
|
|
|
try std.testing.expect(Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx, .cmov }));
|
|
try std.testing.expect(!Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx }));
|
|
try std.testing.expect(Target.x86.featureSetHasAll(target.cpu.features, .{ .mmx, .x87 }));
|
|
try std.testing.expect(!Target.x86.featureSetHasAll(target.cpu.features, .{ .mmx, .x87, .sse }));
|
|
|
|
const text = try cross_target.zigTriple(std.testing.allocator);
|
|
defer std.testing.allocator.free(text);
|
|
try std.testing.expectEqualSlices(u8, "x86_64-linux-gnu", text);
|
|
}
|
|
{
|
|
const cross_target = try CrossTarget.parse(.{
|
|
.arch_os_abi = "arm-linux-musleabihf",
|
|
.cpu_features = "generic+v8a",
|
|
});
|
|
const target = cross_target.toTarget();
|
|
|
|
try std.testing.expect(target.os.tag == .linux);
|
|
try std.testing.expect(target.abi == .musleabihf);
|
|
try std.testing.expect(target.cpu.arch == .arm);
|
|
try std.testing.expect(target.cpu.model == &Target.arm.cpu.generic);
|
|
try std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a));
|
|
|
|
const text = try cross_target.zigTriple(std.testing.allocator);
|
|
defer std.testing.allocator.free(text);
|
|
try std.testing.expectEqualSlices(u8, "arm-linux-musleabihf", text);
|
|
}
|
|
{
|
|
const cross_target = try CrossTarget.parse(.{
|
|
.arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27",
|
|
.cpu_features = "generic+v8a",
|
|
});
|
|
const target = cross_target.toTarget();
|
|
|
|
try std.testing.expect(target.cpu.arch == .aarch64);
|
|
try std.testing.expect(target.os.tag == .linux);
|
|
try std.testing.expect(target.os.version_range.linux.range.min.major == 3);
|
|
try std.testing.expect(target.os.version_range.linux.range.min.minor == 10);
|
|
try std.testing.expect(target.os.version_range.linux.range.min.patch == 0);
|
|
try std.testing.expect(target.os.version_range.linux.range.max.major == 4);
|
|
try std.testing.expect(target.os.version_range.linux.range.max.minor == 4);
|
|
try std.testing.expect(target.os.version_range.linux.range.max.patch == 1);
|
|
try std.testing.expect(target.os.version_range.linux.glibc.major == 2);
|
|
try std.testing.expect(target.os.version_range.linux.glibc.minor == 27);
|
|
try std.testing.expect(target.os.version_range.linux.glibc.patch == 0);
|
|
try std.testing.expect(target.abi == .gnu);
|
|
|
|
const text = try cross_target.zigTriple(std.testing.allocator);
|
|
defer std.testing.allocator.free(text);
|
|
try std.testing.expectEqualSlices(u8, "aarch64-linux.3.10...4.4.1-gnu.2.27", text);
|
|
}
|
|
}
|