From 24ecf45569f10743c5df6a39e6e71dcee265786a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 23 Nov 2024 17:57:39 +0100 Subject: [PATCH] std.Target: Add Os.HurdVersionRange for Os.Tag.hurd. This is necessary since isGnuLibC() is true for hurd, so we need to be able to represent a glibc version for it. Also add an Os.TaggedVersionRange.gnuLibCVersion() convenience function. --- lib/std/Build/Cache.zig | 5 +++ lib/std/Target.zig | 40 ++++++++++++++++--- lib/std/Target/Query.zig | 10 ++--- lib/std/c.zig | 5 +-- lib/std/zig/system.zig | 13 +++--- lib/std/zig/target.zig | 4 +- src/Builtin.zig | 34 ++++++++++++++++ src/Compilation.zig | 2 +- src/codegen/llvm.zig | 16 +++++--- src/glibc.zig | 6 +-- src/libcxx.zig | 6 +-- src/link/Elf.zig | 2 +- test/link/glibc_compat/build.zig | 5 +-- .../link/glibc_compat/glibc_runtime_check.zig | 2 +- 14 files changed, 111 insertions(+), 39 deletions(-) diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 16ecf3c0b0..3f96c5f93c 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -217,6 +217,11 @@ pub const HashHelper = struct { }, std.Target.Os.TaggedVersionRange => { switch (x) { + .hurd => |hurd| { + hh.add(hurd.range.min); + hh.add(hurd.range.max); + hh.add(hurd.glibc); + }, .linux => |linux| { hh.add(linux.range.min); hh.add(linux.range.max); diff --git a/lib/std/Target.zig b/lib/std/Target.zig index b2808255b0..8f2329d541 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -187,7 +187,6 @@ pub const Os = struct { .hermit, .aix, - .hurd, .rtems, .zos, @@ -218,6 +217,7 @@ pub const Os = struct { .vulkan, => .semver, + .hurd => .hurd, .linux => .linux, .windows => .windows, @@ -356,6 +356,21 @@ pub const Os = struct { } }; + pub const HurdVersionRange = struct { + range: std.SemanticVersion.Range, + glibc: std.SemanticVersion, + + pub inline fn includesVersion(range: HurdVersionRange, ver: std.SemanticVersion) bool { + return range.range.includesVersion(ver); + } + + /// Checks if system is guaranteed to be at least `version` or older than `version`. + /// Returns `null` if a runtime check is required. + pub inline fn isAtLeast(range: HurdVersionRange, ver: std.SemanticVersion) ?bool { + return range.range.isAtLeast(ver); + } + }; + pub const LinuxVersionRange = struct { range: std.SemanticVersion.Range, glibc: std.SemanticVersion, @@ -400,6 +415,7 @@ pub const Os = struct { pub const VersionRange = union { none: void, semver: std.SemanticVersion.Range, + hurd: HurdVersionRange, linux: LinuxVersionRange, windows: WindowsVersion.Range, @@ -456,9 +472,12 @@ pub const Os = struct { }, }, .hurd => .{ - .semver = .{ - .min = .{ .major = 0, .minor = 9, .patch = 0 }, - .max = .{ .major = 0, .minor = 9, .patch = 0 }, + .hurd = .{ + .range = .{ + .min = .{ .major = 0, .minor = 9, .patch = 0 }, + .max = .{ .major = 0, .minor = 9, .patch = 0 }, + }, + .glibc = .{ .major = 2, .minor = 28, .patch = 0 }, }, }, .linux => .{ @@ -632,8 +651,17 @@ pub const Os = struct { pub const TaggedVersionRange = union(enum) { none: void, semver: std.SemanticVersion.Range, + hurd: HurdVersionRange, linux: LinuxVersionRange, windows: WindowsVersion.Range, + + pub fn gnuLibCVersion(range: TaggedVersionRange) ?std.SemanticVersion { + return switch (range) { + .none, .semver, .windows => null, + .hurd => |h| h.glibc, + .linux => |l| l.glibc, + }; + } }; /// Provides a tagged union. `Target` does not store the tag because it is @@ -642,6 +670,7 @@ pub const Os = struct { return switch (os.tag.versionRangeTag()) { .none => .{ .none = {} }, .semver => .{ .semver = os.version_range.semver }, + .hurd => .{ .hurd = os.version_range.hurd }, .linux => .{ .linux = os.version_range.linux }, .windows => .{ .windows = os.version_range.windows }, }; @@ -651,12 +680,13 @@ pub const Os = struct { /// Returns `null` if a runtime check is required. pub inline fn isAtLeast(os: Os, comptime tag: Tag, ver: switch (tag.versionRangeTag()) { .none => void, - .semver, .linux => std.SemanticVersion, + .semver, .hurd, .linux => std.SemanticVersion, .windows => WindowsVersion, }) ?bool { return if (os.tag != tag) false else switch (tag.versionRangeTag()) { .none => true, inline .semver, + .hurd, .linux, .windows, => |field| @field(os.version_range, @tagName(field)).isAtLeast(ver), diff --git a/lib/std/Target/Query.zig b/lib/std/Target/Query.zig index f3e280ee32..144b2d60af 100644 --- a/lib/std/Target/Query.zig +++ b/lib/std/Target/Query.zig @@ -102,7 +102,7 @@ pub fn fromTarget(target: Target) Query { .os_version_min = undefined, .os_version_max = undefined, .abi = target.abi, - .glibc_version = if (target.isGnuLibC()) target.os.version_range.linux.glibc else null, + .glibc_version = target.os.versionRange().gnuLibCVersion(), .android_api_level = if (target.abi.isAndroid()) target.os.version_range.linux.android else null, }; result.updateOsVersionRange(target.os); @@ -132,9 +132,9 @@ fn updateOsVersionRange(self: *Query, os: Target.Os) void { .{ .semver = os.version_range.semver.min }, .{ .semver = os.version_range.semver.max }, }, - .linux => .{ - .{ .semver = os.version_range.linux.range.min }, - .{ .semver = os.version_range.linux.range.max }, + inline .hurd, .linux => |t| .{ + .{ .semver = @field(os.version_range, @tagName(t)).range.min }, + .{ .semver = @field(os.version_range, @tagName(t)).range.max }, }, .windows => .{ .{ .windows = os.version_range.windows.min }, @@ -544,7 +544,7 @@ fn parseOs(result: *Query, diags: *ParseOptions.Diagnostics, text: []const u8) ! const version_text = it.rest(); if (version_text.len > 0) switch (tag.versionRangeTag()) { .none => return error.InvalidOperatingSystemVersion, - .semver, .linux => range: { + .semver, .hurd, .linux => range: { var range_it = mem.splitSequence(u8, version_text, "..."); result.os_version_min = .{ .semver = parseVersion(range_it.first()) catch |err| switch (err) { diff --git a/lib/std/c.zig b/lib/std/c.zig index e9961556fb..c6a5a85521 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -56,9 +56,8 @@ pub inline fn versionCheck(comptime version: std.SemanticVersion) bool { if (!builtin.link_libc) break :blk false; if (native_abi.isMusl()) break :blk true; if (builtin.target.isGnuLibC()) { - const ver = builtin.os.version_range.linux.glibc; - const order = ver.order(version); - break :blk switch (order) { + const ver = builtin.os.versionRange().gnuLibCVersion().?; + break :blk switch (ver.order(version)) { .gt, .eq => true, .lt => false, }; diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index d4eb76dc3d..48b195dc3e 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -311,8 +311,8 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { if (query.os_version_min) |min| switch (min) { .none => {}, - .semver => |semver| switch (os.tag) { - .linux => os.version_range.linux.range.min = semver, + .semver => |semver| switch (os.tag.versionRangeTag()) { + inline .hurd, .linux => |t| @field(os.version_range, @tagName(t)).range.min = semver, else => os.version_range.semver.min = semver, }, .windows => |win_ver| os.version_range.windows.min = win_ver, @@ -320,15 +320,18 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { if (query.os_version_max) |max| switch (max) { .none => {}, - .semver => |semver| switch (os.tag) { - .linux => os.version_range.linux.range.max = semver, + .semver => |semver| switch (os.tag.versionRangeTag()) { + inline .hurd, .linux => |t| @field(os.version_range, @tagName(t)).range.max = semver, else => os.version_range.semver.max = semver, }, .windows => |win_ver| os.version_range.windows.max = win_ver, }; if (query.glibc_version) |glibc| { - os.version_range.linux.glibc = glibc; + switch (os.tag.versionRangeTag()) { + inline .hurd, .linux => |t| @field(os.version_range, @tagName(t)).glibc = glibc, + else => {}, + } } if (query.android_api_level) |android| { diff --git a/lib/std/zig/target.zig b/lib/std/zig/target.zig index c72b221936..b9116f557f 100644 --- a/lib/std/zig/target.zig +++ b/lib/std/zig/target.zig @@ -88,10 +88,10 @@ pub fn canBuildLibC(target: std.Target) bool { const ver = target.os.version_range.semver; return ver.min.order(libc.os_ver.?) != .lt; } - // Ensure glibc (aka *-linux-gnu) version is supported + // Ensure glibc (aka *-(linux,hurd)-gnu) version is supported if (target.isGnuLibC()) { const min_glibc_ver = libc.glibc_min orelse return true; - const target_glibc_ver = target.os.version_range.linux.glibc; + const target_glibc_ver = target.os.versionRange().gnuLibCVersion().?; return target_glibc_ver.order(min_glibc_ver) != .lt; } return true; diff --git a/src/Builtin.zig b/src/Builtin.zig index f3018d3023..a212ef4c36 100644 --- a/src/Builtin.zig +++ b/src/Builtin.zig @@ -142,6 +142,40 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { linux.android, }), + .hurd => |hurd| try buffer.writer().print( + \\ .hurd = .{{ + \\ .range = .{{ + \\ .min = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ .max = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ }}, + \\ .glibc = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ }}}}, + \\ + , .{ + hurd.range.min.major, + hurd.range.min.minor, + hurd.range.min.patch, + + hurd.range.max.major, + hurd.range.max.minor, + hurd.range.max.patch, + + hurd.glibc.major, + hurd.glibc.minor, + hurd.glibc.patch, + }), .windows => |windows| try buffer.writer().print( \\ .windows = .{{ \\ .min = {c}, diff --git a/src/Compilation.zig b/src/Compilation.zig index 3ad5c7932f..cc3807b1c9 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -5315,7 +5315,7 @@ pub fn addCCArgs( if (comp.config.link_libc) { if (target.isGnuLibC()) { - const target_version = target.os.version_range.linux.glibc; + const target_version = target.os.versionRange().gnuLibCVersion().?; const glibc_minor_define = try std.fmt.allocPrint(arena, "-D__GLIBC_MINOR__={d}", .{ target_version.minor, }); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index fb20d4d622..e7d7c4923d 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -245,7 +245,7 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![]const u8 { ver.min.minor, ver.min.patch, }), - .linux => |ver| try llvm_triple.writer().print("{d}.{d}.{d}", .{ + inline .linux, .hurd => |ver| try llvm_triple.writer().print("{d}.{d}.{d}", .{ ver.range.min.major, ver.range.min.minor, ver.range.min.patch, @@ -290,11 +290,15 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![]const u8 { .semver, .windows, => {}, - .linux => |ver| if (target.abi.isGnu()) try llvm_triple.writer().print("{d}.{d}.{d}", .{ - ver.glibc.major, - ver.glibc.minor, - ver.glibc.patch, - }) else if (target.abi.isAndroid()) try llvm_triple.writer().print("{d}", .{ver.android}), + inline .hurd, .linux => |ver| if (target.abi.isGnu()) { + try llvm_triple.writer().print("{d}.{d}.{d}", .{ + ver.glibc.major, + ver.glibc.minor, + ver.glibc.patch, + }); + } else if (@TypeOf(ver) == std.Target.Os.LinuxVersionRange and target.abi.isAndroid()) { + try llvm_triple.writer().print("{d}", .{ver.android}); + }, } return llvm_triple.toOwnedSlice(); diff --git a/src/glibc.zig b/src/glibc.zig index f1ac544163..5bad947e5d 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -188,7 +188,7 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre const arena = arena_allocator.allocator(); const target = comp.root_mod.resolved_target.result; - const target_ver = target.os.version_range.linux.glibc; + const target_ver = target.os.versionRange().gnuLibCVersion().?; const nonshared_stat = target_ver.order(.{ .major = 2, .minor = 32, .patch = 0 }) != .gt; const start_old_init_fini = target_ver.order(.{ .major = 2, .minor = 33, .patch = 0 }) != .gt; @@ -750,7 +750,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi const arena = arena_allocator.allocator(); const target = comp.getTarget(); - const target_version = target.os.version_range.linux.glibc; + const target_version = target.os.versionRange().gnuLibCVersion().?; // Use the global cache directory. var cache: Cache = .{ @@ -1218,7 +1218,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi } fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void { - const target_version = comp.getTarget().os.version_range.linux.glibc; + const target_version = comp.getTarget().os.versionRange().gnuLibCVersion().?; assert(comp.glibc_so_files == null); comp.glibc_so_files = so_files; diff --git a/src/libcxx.zig b/src/libcxx.zig index ec89ac1cfc..39fd999112 100644 --- a/src/libcxx.zig +++ b/src/libcxx.zig @@ -262,7 +262,7 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: std.Progress.Node) BuildError! if (target.isGnuLibC()) { // glibc 2.16 introduced aligned_alloc - if (target.os.version_range.linux.glibc.order(.{ .major = 2, .minor = 16, .patch = 0 }) == .lt) { + if (target.os.versionRange().gnuLibCVersion().?.order(.{ .major = 2, .minor = 16, .patch = 0 }) == .lt) { try cflags.append("-D_LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION"); } } @@ -477,7 +477,7 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildErr } try cflags.append("-D_LIBCXXABI_HAS_NO_THREADS"); } else if (target.abi.isGnu()) { - if (target.os.tag != .linux or !(target.os.version_range.linux.glibc.order(.{ .major = 2, .minor = 18, .patch = 0 }) == .lt)) + if (target.os.tag != .linux or !(target.os.versionRange().gnuLibCVersion().?.order(.{ .major = 2, .minor = 18, .patch = 0 }) == .lt)) try cflags.append("-DHAVE___CXA_THREAD_ATEXIT_IMPL"); } @@ -500,7 +500,7 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildErr if (target.isGnuLibC()) { // glibc 2.16 introduced aligned_alloc - if (target.os.version_range.linux.glibc.order(.{ .major = 2, .minor = 16, .patch = 0 }) == .lt) { + if (target.os.versionRange().gnuLibCVersion().?.order(.{ .major = 2, .minor = 16, .patch = 0 }) == .lt) { try cflags.append("-D_LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION"); } } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index a068ac6cdc..608ff2fe3a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2013,7 +2013,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s } else if (target.isGnuLibC()) { for (glibc.libs) |lib| { if (lib.removed_in) |rem_in| { - if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue; + if (target.os.versionRange().gnuLibCVersion().?.order(rem_in) != .lt) continue; } const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{ diff --git a/test/link/glibc_compat/build.zig b/test/link/glibc_compat/build.zig index 29df30151b..e9d7ee412e 100644 --- a/test/link/glibc_compat/build.zig +++ b/test/link/glibc_compat/build.zig @@ -5,10 +5,7 @@ const builtin = @import("builtin"); // run-time glibc version needs to be new enough. Check the host's glibc // version. Note that this does not allow for translation/vm/emulation // services to run these tests. -const running_glibc_ver: ?std.SemanticVersion = switch (builtin.os.tag) { - .linux => builtin.os.version_range.linux.glibc, - else => null, -}; +const running_glibc_ver = builtin.os.versionRange().gnuLibCVersion(); pub fn build(b: *std.Build) void { const test_step = b.step("test", "Test"); diff --git a/test/link/glibc_compat/glibc_runtime_check.zig b/test/link/glibc_compat/glibc_runtime_check.zig index 5a925d8583..14da8acc2c 100644 --- a/test/link/glibc_compat/glibc_runtime_check.zig +++ b/test/link/glibc_compat/glibc_runtime_check.zig @@ -21,7 +21,7 @@ const c_string = @cImport( ); // Version of glibc this test is being built to run against -const glibc_ver = builtin.target.os.version_range.linux.glibc; +const glibc_ver = builtin.os.versionRange().gnuLibCVersion().?; // PR #17034 - fstat moved between libc_nonshared and libc fn checkStat() !void {