From 6bf554f9a75b412f3d1f2306dff8c0036555f08c Mon Sep 17 00:00:00 2001 From: Eric Joldasov Date: Sun, 22 Oct 2023 20:18:20 +0600 Subject: [PATCH] std.zig.system.NativeTargetInfo: fix glibc version parsing In most cases "GLIBC_2.X" strings and `/lib/libc-2.x.so` files do not contain third (`patch`) field, which causes std.SemanticVersion.parse function to return error. To fix this, we reuse [now-public] std.zig.CrossTarget.parseVersion function, which accounts for this third field and makes it 0 in case it was not found. This new behaviour is similar to std.builtin.Version.parse, which was removed in https://github.com/ziglang/zig/commit/6e84f469904a24615a6721265a88ad8dcb4ed83a Fixes regression from https://github.com/ziglang/zig/commit/6e84f469904a24615a6721265a88ad8dcb4ed83a and https://github.com/ziglang/zig/pull/13998 . Related: https://github.com/ziglang/zig/issues/17626 . Results with `zig end`: Before: `"target": "x86_64-linux.6.5.7...6.5.7-gnu.2.19",` After: `"target": "x86_64-linux.6.5.7...6.5.7-gnu.2.36",` Also, while we are here, write explicit error sets and remove duplicate logic from std.zig.system.darwin.macos.parseSystemVersion . Signed-off-by: Eric Joldasov --- lib/std/SemanticVersion.zig | 4 +-- lib/std/zig/CrossTarget.zig | 36 ++++++++++++++----------- lib/std/zig/system/NativeTargetInfo.zig | 19 ++++++++++--- lib/std/zig/system/darwin/macos.zig | 21 +-------------- 4 files changed, 39 insertions(+), 41 deletions(-) diff --git a/lib/std/SemanticVersion.zig b/lib/std/SemanticVersion.zig index 4fa1d47c40..6078661bd6 100644 --- a/lib/std/SemanticVersion.zig +++ b/lib/std/SemanticVersion.zig @@ -140,13 +140,13 @@ pub fn parse(text: []const u8) !Version { return ver; } -fn parseNum(text: []const u8) !usize { +fn parseNum(text: []const u8) error{ InvalidVersion, Overflow }!usize { // Leading zeroes are not allowed. if (text.len > 1 and text[0] == '0') return error.InvalidVersion; return std.fmt.parseUnsigned(usize, text, 10) catch |err| switch (err) { error.InvalidCharacter => return error.InvalidVersion, - else => |e| return e, + error.Overflow => return error.Overflow, }; } diff --git a/lib/std/zig/CrossTarget.zig b/lib/std/zig/CrossTarget.zig index 5a28ebda86..0d6dc30156 100644 --- a/lib/std/zig/CrossTarget.zig +++ b/lib/std/zig/CrossTarget.zig @@ -354,31 +354,37 @@ pub fn parseCpuArch(args: ParseOptions) ?Target.Cpu.Arch { } } -/// Parses a version with an omitted patch component, such as "1.0", -/// which SemanticVersion.parse is not capable of. -fn parseVersion(ver: []const u8) !SemanticVersion { - const parseVersionComponent = struct { - fn parseVersionComponent(component: []const u8) !usize { - return std.fmt.parseUnsigned(usize, component, 10) catch |err| { - switch (err) { - error.InvalidCharacter => return error.InvalidVersion, - error.Overflow => return error.Overflow, - } +/// 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, }; } - }.parseVersionComponent; - var version_components = mem.split(u8, ver, "."); + }).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 parseVersionComponent(major), - .minor = try parseVersionComponent(minor), - .patch = try parseVersionComponent(patch), + .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) { diff --git a/lib/std/zig/system/NativeTargetInfo.zig b/lib/std/zig/system/NativeTargetInfo.zig index bcb60d968e..f2700b48ce 100644 --- a/lib/std/zig/system/NativeTargetInfo.zig +++ b/lib/std/zig/system/NativeTargetInfo.zig @@ -557,7 +557,7 @@ fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion { var buf: [80000]u8 = undefined; if (buf.len < dynstr.size) return error.InvalidGnuLibCVersion; - const dynstr_size = @as(usize, @intCast(dynstr.size)); + const dynstr_size: usize = @intCast(dynstr.size); const dynstr_bytes = buf[0..dynstr_size]; _ = try preadMin(file, dynstr_bytes, dynstr.offset, dynstr_bytes.len); var it = mem.splitScalar(u8, dynstr_bytes, 0); @@ -565,7 +565,7 @@ fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion { while (it.next()) |s| { if (mem.startsWith(u8, s, "GLIBC_2.")) { const chopped = s["GLIBC_".len..]; - const ver = std.SemanticVersion.parse(chopped) catch |err| switch (err) { + const ver = CrossTarget.parseVersion(chopped) catch |err| switch (err) { error.Overflow => return error.InvalidGnuLibCVersion, error.InvalidVersion => return error.InvalidGnuLibCVersion, }; @@ -578,7 +578,7 @@ fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion { return max_ver; } -fn glibcVerFromLinkName(link_name: []const u8, prefix: []const u8) !std.SemanticVersion { +fn glibcVerFromLinkName(link_name: []const u8, prefix: []const u8) error{ UnrecognizedGnuLibCFileName, InvalidGnuLibCVersion }!std.SemanticVersion { // example: "libc-2.3.4.so" // example: "libc-2.27.so" // example: "ld-2.33.so" @@ -588,12 +588,23 @@ fn glibcVerFromLinkName(link_name: []const u8, prefix: []const u8) !std.Semantic } // chop off "libc-" and ".so" const link_name_chopped = link_name[prefix.len .. link_name.len - suffix.len]; - return std.SemanticVersion.parse(link_name_chopped) catch |err| switch (err) { + return CrossTarget.parseVersion(link_name_chopped) catch |err| switch (err) { error.Overflow => return error.InvalidGnuLibCVersion, error.InvalidVersion => return error.InvalidGnuLibCVersion, }; } +test glibcVerFromLinkName { + try std.testing.expectError(error.UnrecognizedGnuLibCFileName, glibcVerFromLinkName("ld-2.37.so", "this-prefix-does-not-exist")); + try std.testing.expectError(error.UnrecognizedGnuLibCFileName, glibcVerFromLinkName("libc-2.37.so-is-not-end", "libc-")); + + try std.testing.expectError(error.InvalidGnuLibCVersion, glibcVerFromLinkName("ld-2.so", "ld-")); + try std.testing.expectEqual(std.SemanticVersion{ .major = 2, .minor = 37, .patch = 0 }, try glibcVerFromLinkName("ld-2.37.so", "ld-")); + try std.testing.expectEqual(std.SemanticVersion{ .major = 2, .minor = 37, .patch = 0 }, try glibcVerFromLinkName("ld-2.37.0.so", "ld-")); + try std.testing.expectEqual(std.SemanticVersion{ .major = 2, .minor = 37, .patch = 1 }, try glibcVerFromLinkName("ld-2.37.1.so", "ld-")); + try std.testing.expectError(error.InvalidGnuLibCVersion, glibcVerFromLinkName("ld-2.37.4.5.so", "ld-")); +} + pub const AbiAndDynamicLinkerFromFileError = error{ FileSystem, SystemResources, diff --git a/lib/std/zig/system/darwin/macos.zig b/lib/std/zig/system/darwin/macos.zig index e513b5f515..2c0b22e96c 100644 --- a/lib/std/zig/system/darwin/macos.zig +++ b/lib/std/zig/system/darwin/macos.zig @@ -87,26 +87,7 @@ fn parseSystemVersion(buf: []const u8) !std.SemanticVersion { const ver = try svt.expectContent(); try svt.skipUntilTag(.end, "string"); - const parseVersionComponent = struct { - fn parseVersionComponent(component: []const u8) !usize { - return std.fmt.parseUnsigned(usize, component, 10) catch |err| { - switch (err) { - error.InvalidCharacter => return error.InvalidVersion, - error.Overflow => return error.Overflow, - } - }; - } - }.parseVersionComponent; - var version_components = mem.split(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 parseVersionComponent(major), - .minor = try parseVersionComponent(minor), - .patch = try parseVersionComponent(patch), - }; + return try std.zig.CrossTarget.parseVersion(ver); } const SystemVersionTokenizer = struct {