From 8c44954bc6a91ae66f5aea92ea8380c28b50b3f0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 4 Dec 2023 20:30:32 -0700 Subject: [PATCH] std.Target.Query: remove deprecated API These functions have been doomed for a long time. Finally I figured out what the proper relationship between this API and std.Target is. --- deps/aro/aro/Driver.zig | 9 +- lib/std/Build.zig | 134 +++------------ lib/std/Build/Module.zig | 4 +- lib/std/Target.zig | 13 +- lib/std/Target/Query.zig | 336 +++++++++++++++----------------------- lib/std/zig.zig | 46 +++++- lib/std/zig/system.zig | 29 +++- src/libc_installation.zig | 13 +- src/main.zig | 16 +- test/tests.zig | 48 +++--- 10 files changed, 275 insertions(+), 373 deletions(-) diff --git a/deps/aro/aro/Driver.zig b/deps/aro/aro/Driver.zig index 18a24b86a7..1738b14093 100644 --- a/deps/aro/aro/Driver.zig +++ b/deps/aro/aro/Driver.zig @@ -366,12 +366,15 @@ pub fn parseArgs( } else if (mem.eql(u8, arg, "-S") or mem.eql(u8, arg, "--assemble")) { d.only_preprocess_and_compile = true; } else if (option(arg, "--target=")) |triple| { - const cross = std.zig.CrossTarget.parse(.{ .arch_os_abi = triple }) catch { + const query = std.Target.Query.parse(.{ .arch_os_abi = triple }) catch { try d.comp.addDiagnostic(.{ .tag = .cli_invalid_target, .extra = .{ .str = arg } }, &.{}); continue; }; - d.comp.target = cross.toTarget(); // TODO deprecated - d.comp.langopts.setEmulatedCompiler(target_util.systemCompiler(d.comp.target)); + const target = std.zig.system.resolveTargetQuery(query) catch |e| { + return d.fatal("unable to resolve target: {s}", .{errorDescription(e)}); + }; + d.comp.target = target; + d.comp.langopts.setEmulatedCompiler(target_util.systemCompiler(target)); d.raw_target_triple = triple; } else if (mem.eql(u8, arg, "--verbose-ast")) { d.verbose_ast = true; diff --git a/lib/std/Build.zig b/lib/std/Build.zig index cb5a503593..96b98b8583 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -383,12 +383,7 @@ fn userInputOptionsFromArgs(allocator: Allocator, args: anytype) UserInputOption }) catch @panic("OOM"); user_input_options.put("cpu", .{ .name = "cpu", - .value = .{ - .scalar = if (v.isNativeCpu()) - "native" - else - serializeCpu(allocator, v.getCpu()) catch unreachable, - }, + .value = .{ .scalar = v.serializeCpuAlloc(allocator) catch @panic("OOM") }, .used = false, }) catch @panic("OOM"); }, @@ -400,12 +395,7 @@ fn userInputOptionsFromArgs(allocator: Allocator, args: anytype) UserInputOption }) catch @panic("OOM"); user_input_options.put("cpu", .{ .name = "cpu", - .value = .{ - .scalar = if (v.query.isNativeCpu()) - "native" - else - serializeCpu(allocator, v.target.cpu) catch unreachable, - }, + .value = .{ .scalar = v.query.serializeCpuAlloc(allocator) catch @panic("OOM") }, .used = false, }) catch @panic("OOM"); }, @@ -1196,7 +1186,6 @@ pub fn standardOptimizeOption(self: *Build, options: StandardOptimizeOptionOptio pub const StandardTargetOptionsArgs = struct { whitelist: ?[]const Target.Query = null, - default_target: Target.Query = .{}, }; @@ -1208,13 +1197,13 @@ pub fn standardTargetOptions(b: *Build, args: StandardTargetOptionsArgs) Resolve } /// Exposes standard `zig build` options for choosing a target. -pub fn standardTargetOptionsQueryOnly(self: *Build, args: StandardTargetOptionsArgs) Target.Query { - const maybe_triple = self.option( +pub fn standardTargetOptionsQueryOnly(b: *Build, args: StandardTargetOptionsArgs) Target.Query { + const maybe_triple = b.option( []const u8, "target", "The CPU architecture, OS, and ABI to build for", ); - const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract"); + const mcpu = b.option([]const u8, "cpu", "Target CPU features to add or subtract"); if (maybe_triple == null and mcpu == null) { return args.default_target; @@ -1236,7 +1225,7 @@ pub fn standardTargetOptionsQueryOnly(self: *Build, args: StandardTargetOptionsA for (diags.arch.?.allCpuModels()) |cpu| { log.err(" {s}", .{cpu.name}); } - self.markInvalidUserInput(); + b.markInvalidUserInput(); return args.default_target; }, error.UnknownCpuFeature => { @@ -1251,7 +1240,7 @@ pub fn standardTargetOptionsQueryOnly(self: *Build, args: StandardTargetOptionsA for (diags.arch.?.allFeaturesList()) |feature| { log.err(" {s}: {s}", .{ feature.name, feature.description }); } - self.markInvalidUserInput(); + b.markInvalidUserInput(); return args.default_target; }, error.UnknownOperatingSystem => { @@ -1263,80 +1252,35 @@ pub fn standardTargetOptionsQueryOnly(self: *Build, args: StandardTargetOptionsA inline for (std.meta.fields(Target.Os.Tag)) |field| { log.err(" {s}", .{field.name}); } - self.markInvalidUserInput(); + b.markInvalidUserInput(); return args.default_target; }, else => |e| { log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) }); - self.markInvalidUserInput(); + b.markInvalidUserInput(); return args.default_target; }, }; - const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch @panic("OOM"); + const whitelist = args.whitelist orelse return selected_target; - if (args.whitelist) |list| whitelist_check: { - // Make sure it's a match of one of the list. - var mismatch_triple = true; - var mismatch_cpu_features = true; - var whitelist_item: Target.Query = .{}; - for (list) |t| { - mismatch_cpu_features = true; - mismatch_triple = true; - - const t_triple = t.zigTriple(self.allocator) catch @panic("OOM"); - if (mem.eql(u8, t_triple, selected_canonicalized_triple)) { - mismatch_triple = false; - whitelist_item = t; - if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) { - mismatch_cpu_features = false; - break :whitelist_check; - } else { - break; - } - } - } - if (mismatch_triple) { - log.err("Chosen target '{s}' does not match one of the supported targets:", .{ - selected_canonicalized_triple, - }); - for (list) |t| { - const t_triple = t.zigTriple(self.allocator) catch @panic("OOM"); - log.err(" {s}", .{t_triple}); - } - } else { - assert(mismatch_cpu_features); - const whitelist_cpu = whitelist_item.getCpu(); - const selected_cpu = selected_target.getCpu(); - log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{ - selected_cpu.model.name, - }); - log.err(" Supported feature Set: ", .{}); - const all_features = whitelist_cpu.arch.allFeaturesList(); - var populated_cpu_features = whitelist_cpu.model.features; - populated_cpu_features.populateDependencies(all_features); - for (all_features, 0..) |feature, i_usize| { - const i = @as(Target.Cpu.Feature.Set.Index, @intCast(i_usize)); - const in_cpu_set = populated_cpu_features.isEnabled(i); - if (in_cpu_set) { - log.err("{s} ", .{feature.name}); - } - } - log.err(" Remove: ", .{}); - for (all_features, 0..) |feature, i_usize| { - const i = @as(Target.Cpu.Feature.Set.Index, @intCast(i_usize)); - const in_cpu_set = populated_cpu_features.isEnabled(i); - const in_actual_set = selected_cpu.features.isEnabled(i); - if (in_actual_set and !in_cpu_set) { - log.err("{s} ", .{feature.name}); - } - } - } - self.markInvalidUserInput(); - return args.default_target; + // Make sure it's a match of one of the list. + for (whitelist) |q| { + if (q.eql(selected_target)) + return selected_target; } - return selected_target; + for (whitelist) |q| { + log.info("allowed target: -Dtarget={s} -Dcpu={s}", .{ + q.zigTriple(b.allocator) catch @panic("OOM"), + q.serializeCpuAlloc(b.allocator) catch @panic("OOM"), + }); + } + log.err("chosen target '{s}' does not match one of the allowed targets", .{ + selected_target.zigTriple(b.allocator) catch @panic("OOM"), + }); + b.markInvalidUserInput(); + return args.default_target; } pub fn addUserInputOption(self: *Build, name_raw: []const u8, value_raw: []const u8) !bool { @@ -2064,34 +2008,6 @@ pub const InstalledFile = struct { } }; -pub fn serializeCpu(allocator: Allocator, cpu: Target.Cpu) ![]const u8 { - // TODO this logic can disappear if cpu model + features becomes part of the target triple - const all_features = cpu.arch.allFeaturesList(); - var populated_cpu_features = cpu.model.features; - populated_cpu_features.populateDependencies(all_features); - - if (populated_cpu_features.eql(cpu.features)) { - // The CPU name alone is sufficient. - return cpu.model.name; - } else { - var mcpu_buffer = ArrayList(u8).init(allocator); - try mcpu_buffer.appendSlice(cpu.model.name); - - for (all_features, 0..) |feature, i_usize| { - const i = @as(Target.Cpu.Feature.Set.Index, @intCast(i_usize)); - const in_cpu_set = populated_cpu_features.isEnabled(i); - const in_actual_set = cpu.features.isEnabled(i); - if (in_cpu_set and !in_actual_set) { - try mcpu_buffer.writer().print("-{s}", .{feature.name}); - } else if (!in_cpu_set and in_actual_set) { - try mcpu_buffer.writer().print("+{s}", .{feature.name}); - } - } - - return try mcpu_buffer.toOwnedSlice(); - } -} - /// This function is intended to be called in the `configure` phase only. /// It returns an absolute directory path, which is potentially going to be a /// source of API breakage in the future, so keep that in mind when using this diff --git a/lib/std/Build/Module.zig b/lib/std/Build/Module.zig index 5482ca25ec..9c98dd0a46 100644 --- a/lib/std/Build/Module.zig +++ b/lib/std/Build/Module.zig @@ -627,12 +627,12 @@ pub fn appendZigProcessFlags( try zig_args.append(@tagName(m.code_model)); } - if (m.target) |target| { + if (m.target) |*target| { // Communicate the query via CLI since it's more compact. if (!target.query.isNative()) { try zig_args.appendSlice(&.{ "-target", try target.query.zigTriple(b.allocator), - "-mcpu", try std.Build.serializeCpu(b.allocator, target.query.getCpu()), + "-mcpu", try target.query.serializeCpuAlloc(b.allocator), }); if (target.query.dynamic_linker.get()) |dynamic_linker| { diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 6f30aa75b1..f8ce63b37f 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -1394,7 +1394,7 @@ pub const Cpu = struct { } }; -pub fn zigTriple(self: Target, allocator: Allocator) ![]u8 { +pub fn zigTriple(self: Target, allocator: Allocator) Allocator.Error![]u8 { return Query.fromTarget(self).zigTriple(allocator); } @@ -1566,11 +1566,20 @@ pub const DynamicLinker = struct { pub fn set(self: *DynamicLinker, dl_or_null: ?[]const u8) void { if (dl_or_null) |dl| { @memcpy(self.buffer[0..dl.len], dl); - self.max_byte = @as(u8, @intCast(dl.len - 1)); + self.max_byte = @intCast(dl.len - 1); } else { self.max_byte = null; } } + + pub fn eql(a: DynamicLinker, b: DynamicLinker) bool { + const a_m = a.max_byte orelse return b.max_byte == null; + const b_m = b.max_byte orelse return false; + if (a_m != b_m) return false; + const a_s = a.buffer[0 .. a_m + 1]; + const b_s = b.buffer[0 .. a_m + 1]; + return std.mem.eql(u8, a_s, b_s); + } }; pub fn standardDynamicLinkerPath(target: Target) DynamicLinker { diff --git a/lib/std/Target/Query.zig b/lib/std/Target/Query.zig index bb5949d597..1deff022cc 100644 --- a/lib/std/Target/Query.zig +++ b/lib/std/Target/Query.zig @@ -51,12 +51,41 @@ pub const CpuModel = union(enum) { determined_by_cpu_arch, explicit: *const Target.Cpu.Model, + + pub fn eql(a: CpuModel, b: CpuModel) bool { + const Tag = @typeInfo(CpuModel).Union.tag_type.?; + const a_tag: Tag = a; + const b_tag: Tag = b; + if (a_tag != b_tag) return false; + return switch (a) { + .native, .baseline, .determined_by_cpu_arch => true, + .explicit => |a_model| a_model == b.explicit, + }; + } }; pub const OsVersion = union(enum) { none: void, semver: SemanticVersion, windows: Target.Os.WindowsVersion, + + pub fn eql(a: OsVersion, b: OsVersion) bool { + const Tag = @typeInfo(OsVersion).Union.tag_type.?; + const a_tag: Tag = a; + const b_tag: Tag = b; + if (a_tag != b_tag) return false; + return switch (a) { + .none => true, + .semver => |a_semver| a_semver.order(b.semver) == .eq, + .windows => |a_windows| a_windows == b.windows, + }; + } + + pub fn eqlOpt(a: ?OsVersion, b: ?OsVersion) bool { + if (a == null and b == null) return true; + if (a == null or b == null) return false; + return OsVersion.eql(a.?, b.?); + } }; pub const SemanticVersion = std.SemanticVersion; @@ -162,16 +191,6 @@ fn updateOsVersionRange(self: *Query, os: Target.Os) void { } } -/// TODO deprecated, use `std.zig.system.resolveTargetQuery`. -pub fn toTarget(self: Query) 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 @@ -240,7 +259,7 @@ pub fn parse(args: ParseOptions) !Query { result.cpu_arch = std.meta.stringToEnum(Target.Cpu.Arch, arch_name) orelse return error.UnknownArchitecture; } - const arch = result.getCpuArch(); + const arch = result.cpu_arch orelse builtin.cpu.arch; diags.arch = arch; if (it.next()) |os_text| { @@ -259,7 +278,7 @@ pub fn parse(args: ParseOptions) !Query { const abi_ver_text = abi_it.rest(); if (abi_it.next() != null) { - if (result.isGnuLibC()) { + if (Target.isGnuLibC_os_tag_abi(result.os_tag orelse builtin.os.tag, abi)) { result.glibc_version = parseVersion(abi_ver_text) catch |err| switch (err) { error.Overflow => return error.InvalidAbiVersion, error.InvalidVersion => return error.InvalidAbiVersion, @@ -377,168 +396,6 @@ test parseVersion { try std.testing.expectError(error.InvalidVersion, parseVersion("1.2.3.4")); } -/// TODO deprecated, use `std.zig.system.resolveTargetQuery`. -pub fn getCpu(self: Query) 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.resolveTargetQuery`. - 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.resolveTargetQuery`. - 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: Query) Target.Cpu.Arch { - return self.cpu_arch orelse builtin.cpu.arch; -} - -pub fn getCpuModel(self: Query) *const Target.Cpu.Model { - return switch (self.cpu_model) { - .explicit => |cpu_model| cpu_model, - else => self.getCpu().model, - }; -} - -pub fn getCpuFeatures(self: Query) Target.Cpu.Feature.Set { - return self.getCpu().features; -} - -/// TODO deprecated, use `std.zig.system.resolveTargetQuery`. -pub fn getOs(self: Query) 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.resolveTargetQuery`. - 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: Query) Target.Os.Tag { - return self.os_tag orelse builtin.os.tag; -} - -/// TODO deprecated, use `std.zig.system.resolveTargetQuery`. -pub fn getOsVersionMin(self: Query) OsVersion { - if (self.os_version_min) |version_min| return version_min; - var tmp: Query = undefined; - tmp.updateOsVersionRange(self.getOs()); - return tmp.os_version_min.?; -} - -/// TODO deprecated, use `std.zig.system.resolveTargetQuery`. -pub fn getOsVersionMax(self: Query) OsVersion { - if (self.os_version_max) |version_max| return version_max; - var tmp: Query = undefined; - tmp.updateOsVersionRange(self.getOs()); - return tmp.os_version_max.?; -} - -/// TODO deprecated, use `std.zig.system.resolveTargetQuery`. -pub fn getAbi(self: Query) 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.resolveTargetQuery`. - return builtin.abi; - } - - return Target.Abi.default(self.getCpuArch(), self.getOs()); -} - -pub fn isFreeBSD(self: Query) bool { - return self.getOsTag() == .freebsd; -} - -pub fn isDarwin(self: Query) bool { - return self.getOsTag().isDarwin(); -} - -pub fn isNetBSD(self: Query) bool { - return self.getOsTag() == .netbsd; -} - -pub fn isOpenBSD(self: Query) bool { - return self.getOsTag() == .openbsd; -} - -pub fn isUefi(self: Query) bool { - return self.getOsTag() == .uefi; -} - -pub fn isDragonFlyBSD(self: Query) bool { - return self.getOsTag() == .dragonfly; -} - -pub fn isLinux(self: Query) bool { - return self.getOsTag() == .linux; -} - -pub fn isWindows(self: Query) bool { - return self.getOsTag() == .windows; -} - -pub fn exeFileExt(self: Query) [:0]const u8 { - return Target.exeFileExtSimple(self.getCpuArch(), self.getOsTag()); -} - -pub fn staticLibSuffix(self: Query) [:0]const u8 { - return Target.staticLibSuffix_os_abi(self.getOsTag(), self.getAbi()); -} - -pub fn dynamicLibSuffix(self: Query) [:0]const u8 { - return self.getOsTag().dynamicLibSuffix(); -} - -pub fn libPrefix(self: Query) [:0]const u8 { - return Target.libPrefix_os_abi(self.getOsTag(), self.getAbi()); -} - pub fn isNativeCpu(self: Query) bool { return self.cpu_arch == null and (self.cpu_model == .native or self.cpu_model == .determined_by_cpu_arch) and @@ -568,7 +425,7 @@ fn formatVersion(version: SemanticVersion, writer: anytype) !void { } } -pub fn zigTriple(self: Query, allocator: mem.Allocator) error{OutOfMemory}![]u8 { +pub fn zigTriple(self: Query, allocator: Allocator) Allocator.Error![]u8 { if (self.isNative()) { return allocator.dupe(u8, "native"); } @@ -583,14 +440,16 @@ pub fn zigTriple(self: Query, allocator: mem.Allocator) error{OutOfMemory}![]u8 // 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()) { + if (self.os_version_min) |min| { + switch (min) { .none => {}, .semver => |v| { try result.writer().writeAll("."); try formatVersion(v, result.writer()); }, - .windows => |v| try result.writer().print("{s}", .{v}), + .windows => |v| { + try result.writer().print("{s}", .{v}); + }, } } if (self.os_version_max) |max| { @@ -600,50 +459,90 @@ pub fn zigTriple(self: Query, allocator: mem.Allocator) error{OutOfMemory}![]u8 try result.writer().writeAll("..."); try formatVersion(v, result.writer()); }, - .windows => |v| try result.writer().print("..{s}", .{v}), + .windows => |v| { + try result.writer().print("...{s}", .{v}); + }, } } if (self.glibc_version) |v| { - try result.writer().print("-{s}.", .{@tagName(self.getAbi())}); + const name = @tagName(self.abi orelse builtin.target.abi); + try result.ensureUnusedCapacity(name.len + 2); + result.appendAssumeCapacity('-'); + result.appendSliceAssumeCapacity(name); + result.appendAssumeCapacity('.'); try formatVersion(v, result.writer()); } else if (self.abi) |abi| { - try result.writer().print("-{s}", .{@tagName(abi)}); + const name = @tagName(abi); + try result.ensureUnusedCapacity(name.len + 1); + result.appendAssumeCapacity('-'); + result.appendSliceAssumeCapacity(name); } return result.toOwnedSlice(); } -pub fn allocDescription(self: Query, allocator: mem.Allocator) ![]u8 { +/// Renders the query into a textual representation that can be parsed via the +/// `-mcpu` flag passed to the Zig compiler. +/// Appends the result to `buffer`. +pub fn serializeCpu(q: Query, buffer: *std.ArrayList(u8)) Allocator.Error!void { + try buffer.ensureUnusedCapacity(8); + switch (q.cpu_model) { + .native => { + buffer.appendSliceAssumeCapacity("native"); + }, + .baseline => { + buffer.appendSliceAssumeCapacity("baseline"); + }, + .determined_by_cpu_arch => { + if (q.cpu_arch == null) { + buffer.appendSliceAssumeCapacity("native"); + } else { + buffer.appendSliceAssumeCapacity("baseline"); + } + }, + .explicit => |model| { + try buffer.appendSlice(model.name); + }, + } + + if (q.cpu_features_add.isEmpty() and q.cpu_features_sub.isEmpty()) { + // The CPU name alone is sufficient. + return; + } + + const cpu_arch = q.cpu_arch orelse builtin.cpu.arch; + const all_features = cpu_arch.allFeaturesList(); + + for (all_features, 0..) |feature, i_usize| { + const i: Target.Cpu.Feature.Set.Index = @intCast(i_usize); + try buffer.ensureUnusedCapacity(feature.name.len + 1); + if (q.cpu_features_sub.isEnabled(i)) { + buffer.appendAssumeCapacity('-'); + buffer.appendSliceAssumeCapacity(feature.name); + } else if (q.cpu_features_add.isEnabled(i)) { + buffer.appendAssumeCapacity('+'); + buffer.appendSliceAssumeCapacity(feature.name); + } + } +} + +pub fn serializeCpuAlloc(q: Query, ally: Allocator) Allocator.Error![]u8 { + var buffer = std.ArrayList(u8).init(ally); + try serializeCpu(q, &buffer); + return buffer.toOwnedSlice(); +} + +pub fn allocDescription(self: Query, allocator: 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: Query, allocator: mem.Allocator) ![]u8 { - return Target.linuxTripleSimple(allocator, self.getCpuArch(), self.getOsTag(), self.getAbi()); -} - -pub fn isGnuLibC(self: Query) bool { - return Target.isGnuLibC_os_tag_abi(self.getOsTag(), self.getAbi()); -} - pub fn setGnuLibCVersion(self: *Query, major: u32, minor: u32, patch: u32) void { - assert(self.isGnuLibC()); self.glibc_version = SemanticVersion{ .major = major, .minor = minor, .patch = patch }; } -pub fn getObjectFormat(self: Query) Target.ObjectFormat { - return self.ofmt orelse Target.ObjectFormat.default(self.getOsTag(), self.getCpuArch()); -} - -pub fn updateCpuFeatures(self: Query, 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: *Query, diags: *ParseOptions.Diagnostics, text: []const u8) !void { var it = mem.splitScalar(u8, text, '.'); const os_name = it.first(); @@ -653,7 +552,7 @@ fn parseOs(result: *Query, diags: *ParseOptions.Diagnostics, text: []const u8) ! result.os_tag = std.meta.stringToEnum(Target.Os.Tag, os_name) orelse return error.UnknownOperatingSystem; } - const tag = result.getOsTag(); + const tag = result.os_tag orelse builtin.os.tag; diags.os_tag = tag; const version_text = it.rest(); @@ -741,12 +640,35 @@ fn parseOs(result: *Query, diags: *ParseOptions.Diagnostics, text: []const u8) ! } } +pub fn eql(a: Query, b: Query) bool { + if (a.cpu_arch != b.cpu_arch) return false; + if (!a.cpu_model.eql(b.cpu_model)) return false; + if (!a.cpu_features_add.eql(b.cpu_features_add)) return false; + if (!a.cpu_features_sub.eql(b.cpu_features_sub)) return false; + if (a.os_tag != b.os_tag) return false; + if (!OsVersion.eqlOpt(a.os_version_min, b.os_version_min)) return false; + if (!OsVersion.eqlOpt(a.os_version_max, b.os_version_max)) return false; + if (!versionEqualOpt(a.glibc_version, b.glibc_version)) return false; + if (a.abi != b.abi) return false; + if (!a.dynamic_linker.eql(b.dynamic_linker)) return false; + if (a.ofmt != b.ofmt) return false; + + return true; +} + +fn versionEqualOpt(a: ?SemanticVersion, b: ?SemanticVersion) bool { + if (a == null and b == null) return true; + if (a == null or b == null) return false; + return SemanticVersion.order(a.?, b.?) == .eq; +} + const Query = @This(); const std = @import("../std.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; const Target = std.Target; const mem = std.mem; +const Allocator = std.mem.Allocator; test parse { if (builtin.target.isGnuLibC()) { @@ -760,7 +682,7 @@ test parse { const triple = std.fmt.bufPrint( buf[0..], "native-native-{s}.2.1.1", - .{@tagName(builtin.abi)}, + .{@tagName(builtin.target.abi)}, ) catch unreachable; try std.testing.expectEqualSlices(u8, triple, text); @@ -789,7 +711,7 @@ test parse { .arch_os_abi = "x86_64-linux-gnu", .cpu_features = "x86_64-sse-sse2-avx-cx8", }); - const target = query.toTarget(); + const target = try std.zig.system.resolveTargetQuery(query); try std.testing.expect(target.os.tag == .linux); try std.testing.expect(target.abi == .gnu); @@ -814,7 +736,7 @@ test parse { .arch_os_abi = "arm-linux-musleabihf", .cpu_features = "generic+v8a", }); - const target = query.toTarget(); + const target = try std.zig.system.resolveTargetQuery(query); try std.testing.expect(target.os.tag == .linux); try std.testing.expect(target.abi == .musleabihf); @@ -831,7 +753,7 @@ test parse { .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27", .cpu_features = "generic+v8a", }); - const target = query.toTarget(); + const target = try std.zig.system.resolveTargetQuery(query); try std.testing.expect(target.cpu.arch == .aarch64); try std.testing.expect(target.os.tag == .linux); diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 481768bfb2..84feb2cf0a 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -1,7 +1,4 @@ -const std = @import("std.zig"); -const tokenizer = @import("zig/tokenizer.zig"); pub const fmt = @import("zig/fmt.zig"); -const assert = std.debug.assert; pub const ErrorBundle = @import("zig/ErrorBundle.zig"); pub const Server = @import("zig/Server.zig"); @@ -115,7 +112,7 @@ pub const BinNameOptions = struct { }; /// Returns the standard file system basename of a binary generated by the Zig compiler. -pub fn binNameAlloc(allocator: std.mem.Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 { +pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 { const root_name = options.root_name; const target = options.target; switch (target.ofmt) { @@ -281,6 +278,47 @@ pub const BuildId = union(enum) { } }; +/// Renders a `std.Target.Cpu` value into a textual representation that can be parsed +/// via the `-mcpu` flag passed to the Zig compiler. +/// Appends the result to `buffer`. +pub fn serializeCpu(buffer: *std.ArrayList(u8), cpu: std.Target.Cpu) Allocator.Error!void { + const all_features = cpu.arch.allFeaturesList(); + var populated_cpu_features = cpu.model.features; + populated_cpu_features.populateDependencies(all_features); + + try buffer.appendSlice(cpu.model.name); + + if (populated_cpu_features.eql(cpu.features)) { + // The CPU name alone is sufficient. + return; + } + + for (all_features, 0..) |feature, i_usize| { + const i: std.Target.Cpu.Feature.Set.Index = @intCast(i_usize); + const in_cpu_set = populated_cpu_features.isEnabled(i); + const in_actual_set = cpu.features.isEnabled(i); + try buffer.ensureUnusedCapacity(feature.name.len + 1); + if (in_cpu_set and !in_actual_set) { + buffer.appendAssumeCapacity('-'); + buffer.appendSliceAssumeCapacity(feature.name); + } else if (!in_cpu_set and in_actual_set) { + buffer.appendAssumeCapacity('+'); + buffer.appendSliceAssumeCapacity(feature.name); + } + } +} + +pub fn serializeCpuAlloc(ally: Allocator, cpu: std.Target.Cpu) Allocator.Error![]u8 { + var buffer = std.ArrayList(u8).init(ally); + try serializeCpu(&buffer, cpu); + return buffer.toOwnedSlice(); +} + +const std = @import("std.zig"); +const tokenizer = @import("zig/tokenizer.zig"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; + test { @import("std").testing.refAllDecls(@This()); } diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 6920999ef0..c2a9fa4f9f 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -163,7 +163,8 @@ pub const DetectError = error{ /// components by detecting the native system, and then resolves /// standard/default parts relative to that. pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { - var os = query.getOsTag().defaultVersionRange(query.getCpuArch()); + const query_os_tag = query.os_tag orelse builtin.os.tag; + var os = query_os_tag.defaultVersionRange(query.cpu_arch orelse builtin.cpu.arch); if (query.os_tag == null) { switch (builtin.target.os.tag) { .linux => { @@ -292,7 +293,7 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { if (query.os_version_min) |min| switch (min) { .none => {}, - .semver => |semver| switch (query.getOsTag()) { + .semver => |semver| switch (os.tag) { .linux => os.version_range.linux.range.min = semver, else => os.version_range.semver.min = semver, }, @@ -301,7 +302,7 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { if (query.os_version_max) |max| switch (max) { .none => {}, - .semver => |semver| switch (query.getOsTag()) { + .semver => |semver| switch (os.tag) { .linux => os.version_range.linux.range.max = semver, else => os.version_range.semver.max = semver, }, @@ -309,13 +310,12 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { }; if (query.glibc_version) |glibc| { - assert(query.isGnuLibC()); os.version_range.linux.glibc = glibc; } // Until https://github.com/ziglang/zig/issues/4592 is implemented (support detecting the // native CPU architecture as being different than the current target), we use this: - const cpu_arch = query.getCpuArch(); + const cpu_arch = query.cpu_arch orelse builtin.cpu.arch; const cpu = switch (query.cpu_model) { .native => detectNativeCpuAndFeatures(cpu_arch, os, query), @@ -361,10 +361,27 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { }, else => {}, } - query.updateCpuFeatures(&result.cpu.features); + updateCpuFeatures( + &result.cpu.features, + cpu_arch.allFeaturesList(), + query.cpu_features_add, + query.cpu_features_sub, + ); return result; } +fn updateCpuFeatures( + set: *Target.Cpu.Feature.Set, + all_features_list: []const Target.Cpu.Feature, + add_set: Target.Cpu.Feature.Set, + sub_set: Target.Cpu.Feature.Set, +) void { + set.removeFeatureSet(sub_set); + set.addFeatureSet(add_set); + set.populateDependencies(all_features_list); + set.removeFeatureSet(sub_set); +} + fn detectNativeCpuAndFeatures(cpu_arch: Target.Cpu.Arch, os: Target.Os, query: Target.Query) ?Target.Cpu { // Here we switch on a comptime value rather than `cpu_arch`. This is valid because `cpu_arch`, // although it is a runtime value, is guaranteed to be one of the architectures in the set diff --git a/src/libc_installation.zig b/src/libc_installation.zig index facb16257e..0901194dd8 100644 --- a/src/libc_installation.zig +++ b/src/libc_installation.zig @@ -41,7 +41,7 @@ pub const LibCInstallation = struct { pub fn parse( allocator: Allocator, libc_file: []const u8, - target: std.Target.Query, + target: std.Target, ) !LibCInstallation { var self: LibCInstallation = .{}; @@ -95,24 +95,23 @@ pub const LibCInstallation = struct { return error.ParseError; } - const os_tag = target.getOsTag(); + const os_tag = target.os.tag; if (self.crt_dir == null and !target.isDarwin()) { log.err("crt_dir may not be empty for {s}\n", .{@tagName(os_tag)}); return error.ParseError; } - const abi = target.getAbi(); - if (self.msvc_lib_dir == null and target.isWindows() and abi == .msvc) { + if (self.msvc_lib_dir == null and os_tag == .windows and target.abi == .msvc) { log.err("msvc_lib_dir may not be empty for {s}-{s}\n", .{ @tagName(os_tag), - @tagName(abi), + @tagName(target.abi), }); return error.ParseError; } - if (self.kernel32_lib_dir == null and target.isWindows() and abi == .msvc) { + if (self.kernel32_lib_dir == null and os_tag == .windows and target.abi == .msvc) { log.err("kernel32_lib_dir may not be empty for {s}-{s}\n", .{ @tagName(os_tag), - @tagName(abi), + @tagName(target.abi), }); return error.ParseError; } diff --git a/src/main.zig b/src/main.zig index 716be60763..3abe6ed3e4 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2696,13 +2696,13 @@ fn buildOutputType( } if (use_lld) |opt| { - if (opt and target_query.isDarwin()) { + if (opt and target.isDarwin()) { fatal("LLD requested with Mach-O object format. Only the self-hosted linker is supported for this target.", .{}); } } if (want_lto) |opt| { - if (opt and target_query.isDarwin()) { + 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", .{}); } } @@ -2772,7 +2772,7 @@ fn buildOutputType( var libc_installation: ?LibCInstallation = null; if (libc_paths_file) |paths_file| { - libc_installation = LibCInstallation.parse(arena, paths_file, target_query) catch |err| { + 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) }); }; } @@ -2865,7 +2865,7 @@ fn buildOutputType( libc_installation = try LibCInstallation.findNative(.{ .allocator = arena, .verbose = true, - .target = target_query.toTarget(), + .target = target, }); try lib_dirs.appendSlice(&.{ libc_installation.?.msvc_lib_dir.?, libc_installation.?.kernel32_lib_dir.? }); @@ -4755,6 +4755,7 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void { const target_query = try parseTargetQueryOrReportFatalError(gpa, .{ .arch_os_abi = target_arch_os_abi, }); + const target = try std.zig.system.resolveTargetQuery(target_query); if (print_includes) { var arena_state = std.heap.ArenaAllocator.init(gpa); @@ -4764,7 +4765,7 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void { const libc_installation: ?*LibCInstallation = libc: { if (input_file) |libc_file| { const libc = try arena.create(LibCInstallation); - libc.* = LibCInstallation.parse(arena, libc_file, target_query) catch |err| { + libc.* = LibCInstallation.parse(arena, libc_file, target) catch |err| { fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) }); }; break :libc libc; @@ -4779,7 +4780,6 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void { }; defer zig_lib_directory.handle.close(); - const target = target_query.toTarget(); const is_native_abi = target_query.isNativeAbi(); const libc_dirs = Compilation.detectLibCIncludeDirs( @@ -4810,7 +4810,7 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void { } if (input_file) |libc_file| { - var libc = LibCInstallation.parse(gpa, libc_file, target_query) catch |err| { + var libc = LibCInstallation.parse(gpa, libc_file, target) catch |err| { fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) }); }; defer libc.deinit(gpa); @@ -4821,7 +4821,7 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void { var libc = LibCInstallation.findNative(.{ .allocator = gpa, .verbose = true, - .target = try std.zig.system.resolveTargetQuery(target_query), + .target = target, }) catch |err| { fatal("unable to detect native libc: {s}", .{@errorName(err)}); }; diff --git a/test/tests.zig b/test/tests.zig index 29d181c605..64c0f3a42e 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1036,14 +1036,17 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { for (test_targets) |test_target| { const is_native = test_target.target.isNative() or - (test_target.target.getOsTag() == builtin.os.tag and - test_target.target.getCpuArch() == builtin.cpu.arch); + (test_target.target.os_tag == builtin.os.tag and + test_target.target.cpu_arch == builtin.cpu.arch); if (options.skip_non_native and !is_native) continue; + const resolved_target = b.resolveTargetQuery(test_target.target); + const target = resolved_target.target; + if (options.skip_cross_glibc and !test_target.target.isNative() and - test_target.target.isGnuLibC() and test_target.link_libc == true) + target.isGnuLibC() and test_target.link_libc == true) continue; if (options.skip_libc and test_target.link_libc == true) @@ -1053,35 +1056,30 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { continue; // TODO get compiler-rt tests passing for self-hosted backends. - if ((test_target.target.getCpuArch() != .x86_64 or - test_target.target.getObjectFormat() != .elf) and + if ((target.cpu.arch != .x86_64 or target.ofmt != .elf) and test_target.use_llvm == false and mem.eql(u8, options.name, "compiler-rt")) continue; // TODO get compiler-rt tests passing for wasm32-wasi // currently causes "LLVM ERROR: Unable to expand fixed point multiplication." - if (test_target.target.getCpuArch() == .wasm32 and - test_target.target.getOsTag() == .wasi and + if (target.cpu.arch == .wasm32 and target.os.tag == .wasi and mem.eql(u8, options.name, "compiler-rt")) { continue; } // TODO get universal-libc tests passing for other self-hosted backends. - if (test_target.target.getCpuArch() != .x86_64 and + if (target.cpu.arch != .x86_64 and test_target.use_llvm == false and mem.eql(u8, options.name, "universal-libc")) continue; // TODO get std lib tests passing for other self-hosted backends. - if ((test_target.target.getCpuArch() != .x86_64 or - test_target.target.getOsTag() != .linux) and + if ((target.cpu.arch != .x86_64 or target.os.tag != .linux) and test_target.use_llvm == false and mem.eql(u8, options.name, "std")) continue; - if (test_target.target.getCpuArch() == .x86_64 and - test_target.target.getOsTag() == .windows and - test_target.target.cpu_arch == null and - test_target.optimize_mode != .Debug and + if (target.cpu.arch == .x86_64 and target.os.tag == .windows and + test_target.target.cpu_arch == null and test_target.optimize_mode != .Debug and mem.eql(u8, options.name, "std")) { // https://github.com/ziglang/zig/issues/17902 @@ -1094,11 +1092,11 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { if (!want_this_mode) continue; const libc_suffix = if (test_target.link_libc == true) "-libc" else ""; - const triple_txt = test_target.target.zigTriple(b.allocator) catch @panic("OOM"); - const model_txt = test_target.target.getCpuModel().name; + const triple_txt = target.zigTriple(b.allocator) catch @panic("OOM"); + const model_txt = target.cpu.model.name; // wasm32-wasi builds need more RAM, idk why - const max_rss = if (test_target.target.getOs().tag == .wasi) + const max_rss = if (target.os.tag == .wasi) options.max_rss * 2 else options.max_rss; @@ -1106,7 +1104,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { const these_tests = b.addTest(.{ .root_source_file = .{ .path = options.root_src }, .optimize = test_target.optimize_mode, - .target = b.resolveTargetQuery(test_target.target), + .target = resolved_target, .max_rss = max_rss, .filter = options.test_filter, .link_libc = test_target.link_libc, @@ -1120,7 +1118,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { const single_threaded_suffix = if (test_target.single_threaded == true) "-single" else ""; const backend_suffix = if (test_target.use_llvm == true) "-llvm" - else if (test_target.target.ofmt == std.Target.ObjectFormat.c) + else if (target.ofmt == std.Target.ObjectFormat.c) "-cbe" else if (test_target.use_llvm == false) "-selfhosted" @@ -1131,7 +1129,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { these_tests.addIncludePath(.{ .path = "test" }); - if (test_target.target.getOs().tag == .wasi) { + if (target.os.tag == .wasi) { // WASI's default stack size can be too small for some big tests. these_tests.stack_size = 2 * 1024 * 1024; } @@ -1148,14 +1146,14 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { use_pic, }); - if (test_target.target.ofmt == std.Target.ObjectFormat.c) { - var altered_target = test_target.target; - altered_target.ofmt = null; + if (target.ofmt == std.Target.ObjectFormat.c) { + var altered_query = test_target.target; + altered_query.ofmt = null; const compile_c = b.addExecutable(.{ .name = qualified_name, .link_libc = test_target.link_libc, - .target = b.resolveTargetQuery(altered_target), + .target = b.resolveTargetQuery(altered_query), .zig_lib_dir = .{ .path = "lib" }, }); compile_c.addCSourceFile(.{ @@ -1179,7 +1177,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { }, }); compile_c.addIncludePath(.{ .path = "lib" }); // for zig.h - if (test_target.target.getOsTag() == .windows) { + if (target.os.tag == .windows) { if (true) { // Unfortunately this requires about 8G of RAM for clang to compile // and our Windows CI runners do not have this much.