Commit 24ecf45569

Alex Rønne Petersen <alex@alexrp.com>
2024-11-23 17:57:39
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.
1 parent 6d781e0
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);
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) {
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| {
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;
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,
             };
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),
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();
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}", .{
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},
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,
             });
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;
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");
             }
         }
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");
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 {