From f2f5a20a53c84d35aee368e0253e6bc03f1a05dd Mon Sep 17 00:00:00 2001 From: seanthegleaming Date: Mon, 27 Oct 2025 13:16:45 -0400 Subject: [PATCH 1/5] Remove allocator parameter from resolveTargetQuery Instead of duplicating the prerelease and build components, we now ignore them altogether. This is implemented in Target.Query.parseVersion, which now additionally discards the prerelease and build components. --- lib/std/Target/Query.zig | 16 +++++++++++--- lib/std/zig/system.zig | 33 +++++++++-------------------- lib/std/zig/system/darwin/macos.zig | 2 ++ 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/lib/std/Target/Query.zig b/lib/std/Target/Query.zig index aa6671b65f29..3d3576726532 100644 --- a/lib/std/Target/Query.zig +++ b/lib/std/Target/Query.zig @@ -339,6 +339,7 @@ pub fn parseCpuArch(args: ParseOptions) ?Target.Cpu.Arch { /// 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. +/// * Prerelease and build components are ignored pub fn parseVersion(ver: []const u8) error{ InvalidVersion, Overflow }!SemanticVersion { const parseVersionComponentFn = (struct { fn parseVersionComponentInner(component: []const u8) error{ InvalidVersion, Overflow }!usize { @@ -348,11 +349,16 @@ pub fn parseVersion(ver: []const u8) error{ InvalidVersion, Overflow }!SemanticV }; } }).parseVersionComponentInner; - var version_components = mem.splitScalar(u8, ver, '.'); + + const end = mem.findAny(u8, ver, "+-"); + const ver_stripped = ver[0 .. end orelse ver.len]; + + var version_components = mem.splitScalar(u8, ver_stripped, '.'); + 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), @@ -361,9 +367,13 @@ pub fn parseVersion(ver: []const u8) error{ InvalidVersion, Overflow }!SemanticV } 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.expectEqual(SemanticVersion{ .major = 1, .minor = 2, .patch = 3 }, parseVersion("1.2.3-pre+build")); + try std.testing.expectEqual(SemanticVersion{ .major = 1, .minor = 2, .patch = 3 }, parseVersion("1.2.3-dev.4.5+a.b.c")); + + try std.testing.expectError(error.InvalidVersion, parseVersion("1")); try std.testing.expectError(error.InvalidVersion, parseVersion("1.2.3.4")); } diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index f5fae5581a70..cd19619229e1 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -215,12 +215,14 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { var os = query_os_tag.defaultVersionRange(query_cpu_arch, query_abi); if (query.os_tag == null) { switch (builtin.target.os.tag) { - .linux => { + .linux, .solaris, .illumos => { const uts = posix.uname(); const release = mem.sliceTo(&uts.release, 0); // The release field sometimes has a weird format, - // `Version.parse` will attempt to find some meaningful interpretation. - if (std.SemanticVersion.parse(release)) |ver| { + // `Target.Query.parseVersion` will attempt to find some meaningful interpretation. + if (Target.Query.parseVersion(release)) |ver| { + assert(ver.pre == null); + assert(ver.build == null); os.version_range.linux.range.min = ver; os.version_range.linux.range.max = ver; } else |err| switch (err) { @@ -228,17 +230,6 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { error.InvalidVersion => {}, } }, - .solaris, .illumos => { - const uts = posix.uname(); - const release = mem.sliceTo(&uts.release, 0); - if (std.SemanticVersion.parse(release)) |ver| { - os.version_range.semver.min = ver; - os.version_range.semver.max = ver; - } else |err| switch (err) { - error.Overflow => {}, - error.InvalidVersion => {}, - } - }, .windows => { const detected_version = windows.detectRuntimeVersion(); os.version_range.windows.min = detected_version; @@ -307,10 +298,9 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { posix.CTL.KERN, posix.KERN.OSRELEASE, }; - var buf: [64]u8 = undefined; + var buf: [64:0]u8 = undefined; // consider that sysctl result includes null-termination - // reserve 1 byte to ensure we never overflow when appending ".0" - var len: usize = buf.len - 1; + var len: usize = buf.len + 1; posix.sysctl(&mib, &buf, &len, null, 0) catch |err| switch (err) { error.NameTooLong => unreachable, // constant, known good value @@ -320,12 +310,9 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { error.Unexpected => return error.OSVersionDetectionFail, }; - // append ".0" to satisfy semver - buf[len - 1] = '.'; - buf[len] = '0'; - len += 1; - - if (std.SemanticVersion.parse(buf[0..len])) |ver| { + if (Target.Query.parse(buf[0..len :0])) |ver| { + assert(ver.build == null); + assert(ver.pre == null); os.version_range.semver.min = ver; os.version_range.semver.max = ver; } else |_| { diff --git a/lib/std/zig/system/darwin/macos.zig b/lib/std/zig/system/darwin/macos.zig index eecb857465ad..9bb4e34e3bc3 100644 --- a/lib/std/zig/system/darwin/macos.zig +++ b/lib/std/zig/system/darwin/macos.zig @@ -58,6 +58,8 @@ pub fn detect(target_os: *Target.Os) !void { if (parseSystemVersion(bytes)) |ver| { // never return non-canonical `10.(16+)` if (!(ver.major == 10 and ver.minor >= 16)) { + assert(ver.pre == null); + assert(ver.build == null); target_os.version_range.semver.min = ver; target_os.version_range.semver.max = ver; return; From b14df9f16f01acb5392d8e07ff2cc2ccc30ac65e Mon Sep 17 00:00:00 2001 From: seanthegleaming Date: Mon, 27 Oct 2025 13:30:33 -0400 Subject: [PATCH 2/5] Reintroduce validating the end of the version in Query.parseVersion This was removed while implementing the ignoring of prerelease and build components, but currently it is incorrect to leave out --- lib/std/Target/Query.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/Target/Query.zig b/lib/std/Target/Query.zig index 3d3576726532..a032aceb02ab 100644 --- a/lib/std/Target/Query.zig +++ b/lib/std/Target/Query.zig @@ -358,6 +358,7 @@ pub fn parseVersion(ver: []const u8) error{ InvalidVersion, Overflow }!SemanticV 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), From ac7f9455f0fbc4260a48b49cbdd74b63c0c940c0 Mon Sep 17 00:00:00 2001 From: seanthegleaming Date: Mon, 27 Oct 2025 14:08:30 -0400 Subject: [PATCH 3/5] Remove solaris usage, disallow pre/build in Query.parseVersion --- lib/std/Target/Query.zig | 7 ++----- lib/std/zig/system.zig | 15 ++++++++------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/std/Target/Query.zig b/lib/std/Target/Query.zig index a032aceb02ab..c4e2cc2ee1a8 100644 --- a/lib/std/Target/Query.zig +++ b/lib/std/Target/Query.zig @@ -339,7 +339,7 @@ pub fn parseCpuArch(args: ParseOptions) ?Target.Cpu.Arch { /// 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. -/// * Prerelease and build components are ignored +/// * Prerelease and build components are disallowed. pub fn parseVersion(ver: []const u8) error{ InvalidVersion, Overflow }!SemanticVersion { const parseVersionComponentFn = (struct { fn parseVersionComponentInner(component: []const u8) error{ InvalidVersion, Overflow }!usize { @@ -350,10 +350,7 @@ pub fn parseVersion(ver: []const u8) error{ InvalidVersion, Overflow }!SemanticV } }).parseVersionComponentInner; - const end = mem.findAny(u8, ver, "+-"); - const ver_stripped = ver[0 .. end orelse ver.len]; - - var version_components = mem.splitScalar(u8, ver_stripped, '.'); + var version_components = mem.splitScalar(u8, ver, '.'); const major = version_components.first(); const minor = version_components.next() orelse return error.InvalidVersion; diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 70f158115d7c..e64ff29a3b9a 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -215,16 +215,17 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { var os = query_os_tag.defaultVersionRange(query_cpu_arch, query_abi); if (query.os_tag == null) { switch (builtin.target.os.tag) { - .linux, .solaris, .illumos => { + .linux, .illumos => { const uts = posix.uname(); const release = mem.sliceTo(&uts.release, 0); // The release field sometimes has a weird format, - // `Target.Query.parseVersion` will attempt to find some meaningful interpretation. - if (Target.Query.parseVersion(release)) |ver| { - assert(ver.pre == null); - assert(ver.build == null); - os.version_range.linux.range.min = ver; - os.version_range.linux.range.max = ver; + // `Version.parse` will attempt to find some meaningful interpretation. + if (std.SemanticVersion.parse(release)) |ver| { + var stripped = ver; + stripped.pre = null; + stripped.build = null; + os.version_range.linux.range.min = stripped; + os.version_range.linux.range.max = stripped; } else |err| switch (err) { error.Overflow => {}, error.InvalidVersion => {}, From bd91fd04559370327fff3cb4b9417ebe1acaea56 Mon Sep 17 00:00:00 2001 From: seanthegleaming Date: Mon, 27 Oct 2025 14:58:15 -0400 Subject: [PATCH 4/5] Update prerelease/build test in Target.Query.parseVersion Now the test ensures that the function returns an error rather than discarding the components --- lib/std/Target/Query.zig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/std/Target/Query.zig b/lib/std/Target/Query.zig index c4e2cc2ee1a8..90c127d80089 100644 --- a/lib/std/Target/Query.zig +++ b/lib/std/Target/Query.zig @@ -368,11 +368,9 @@ test parseVersion { 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.expectEqual(SemanticVersion{ .major = 1, .minor = 2, .patch = 3 }, parseVersion("1.2.3-pre+build")); - try std.testing.expectEqual(SemanticVersion{ .major = 1, .minor = 2, .patch = 3 }, parseVersion("1.2.3-dev.4.5+a.b.c")); - try std.testing.expectError(error.InvalidVersion, parseVersion("1")); try std.testing.expectError(error.InvalidVersion, parseVersion("1.2.3.4")); + try std.testing.expectError(error.InvalidVersion, parseVersion("1.2.3-dev")); } pub fn isNativeCpu(self: Query) bool { From 4bd7530b5201eb7dfa69b97f947d213eec450770 Mon Sep 17 00:00:00 2001 From: Sean <69403556+SeanTUT@users.noreply.github.com> Date: Mon, 27 Oct 2025 23:42:59 -0400 Subject: [PATCH 5/5] Correct `Target.query.parseVersion` typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alex Rønne Petersen --- lib/std/zig/system.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index e64ff29a3b9a..bc44bbdd4639 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -311,7 +311,7 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { error.Unexpected => return error.OSVersionDetectionFail, }; - if (Target.Query.parse(buf[0..len :0])) |ver| { + if (Target.Query.parseVersion(buf[0..len :0])) |ver| { assert(ver.build == null); assert(ver.pre == null); os.version_range.semver.min = ver;