lang/zig: Find versioned shared libraries on OpenBSD

Volker Schlecht <[email protected]>
Newsgroups gmane.os.openbsd.ports
Message-ID <[email protected]>
Hi,


as Robert Lillack remarked in his submission of wayland/river:

    The Zig build system currently does not correctly work for linking
    to system libraries in OpenBSD without specifying a version
    number, so a patch for River's build system is necessary.
    https://marc.info/?l=openbsd-ports&m=177944156665691&w=2

There have been prior attempts to make that work on OpenBSD, but it seems
that interest in the feature was lost in early 2024:

    https://github.com/ziglang/zig/pull/18475

Attached is a second attempt. Both as a diff to lang/zig and as a PR on
codeberg: https://codeberg.org/ziglang/zig/pulls/36135

What I can say is that this *works*. I can drop the patch in wayland/river
and it builds and runs fine without.

What I suspect however is that this looks like what it is: A hodgepodge of
Robert's diff and the originally proposed PR that I came up with after reading
a few tutorials.

So if anyone here is a more experienced zig programmer, please review, fix, 
rewrite or weigh in on the PR :-)

NB: I am looking for feedback from experienced *human* zig programmers (see
zig's no-AI policy).

cheers,
Volker
zig-dynlib.diff (text/x-patch, 4 KB)
Index: Makefile
===================================================================
RCS file: /cvs/ports/lang/zig/Makefile,v
retrieving revision 1.32
diff -u -p -r1.32 Makefile
--- Makefile	11 May 2026 21:18:06 -0000	1.32
+++ Makefile	14 Jul 2026 20:02:33 -0000
@@ -7,6 +7,7 @@ COMMENT =	zig compiler and toolchain
 
 V =		0.16.0
 DIST_TUPLE = 	codeberg ziglang zig ${V} .
+REVISION =	0
 
 # see https://codeberg.org/ziglang/zig/src/tag/0.16.0
 ZIG_COMMIT =	24fdd5b7a4c1c8b5deb5b56756b9dbc8e08c86a8
Index: distinfo
===================================================================
RCS file: /cvs/ports/lang/zig/distinfo,v
retrieving revision 1.16
diff -u -p -r1.16 distinfo
--- distinfo	11 May 2026 21:18:06 -0000	1.16
+++ distinfo	14 Jul 2026 20:02:33 -0000
@@ -1,2 +1,2 @@
-SHA256 (ziglang-zig-0.16.0.tar.gz) = CV10gwCOiF5l2Malf7l8VzRPmVQIpki41Q9hJ4rYXX8=
-SIZE (ziglang-zig-0.16.0.tar.gz) = 35262315
+SHA256 (ziglang-zig-0.16.0.tar.gz) = /b3OcQUn7dIBhXCYf8Kb2dbJ6CxRyykHudG9cMmT+l8=
+SIZE (ziglang-zig-0.16.0.tar.gz) = 35594555
Index: patches/patch-src_link_zig
===================================================================
RCS file: patches/patch-src_link_zig
diff -N patches/patch-src_link_zig
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ patches/patch-src_link_zig	14 Jul 2026 20:02:33 -0000
@@ -0,0 +1,67 @@
+Index: src/link.zig
+--- src/link.zig.orig
++++ src/link.zig
+@@ -2148,6 +2148,63 @@ fn resolveLibInput(
+         return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query);
+     }
+ 
++    // In the case of OpenBSD, dynamic libraries are always versioned, without
++    // unversioned symlinks, so we need to look for the highest-versioned shared
++    // library.
++    if (target.isOpenBSDLibC() and link_mode == .dynamic) versioned: {
++        const prefix = try std.fmt.allocPrint(arena, "lib{s}.so.", .{lib_name});
++
++        var dir = lib_directory.handle.openDir(io, ".", .{ .iterate = true }) catch |err| switch (err) {
++            error.NotDir, error.FileNotFound => break :versioned,
++            else => |e| fatal("unable to search for shared library '{s}.*': {s}", .{ prefix, @errorName(e) }),
++        };
++        defer dir.close(io);
++
++        var best_match_version = std.SemanticVersion{
++            .major = 0,
++            .minor = 0,
++            .patch = 0,
++        };
++        var best_match: ?[]const u8 = null;
++
++        var iter = dir.iterate();
++        while (iter.next(io) catch |err| {
++            fatal("unable to scan library directory '{s}'", .{@errorName(err)});
++        }) |entry| {
++            if (entry.kind != .file) continue;
++            if (!std.mem.startsWith(u8, entry.name, prefix)) continue;
++
++            const rest = entry.name[prefix.len..];
++            var sit = std.mem.splitScalar(u8, rest, '.');
++            const major_str = sit.next() orelse continue;
++            const minor_str = sit.next() orelse continue;
++            if (sit.next() != null) continue;
++            const major = std.fmt.parseInt(usize, major_str, 10) catch continue;
++            const minor = std.fmt.parseInt(usize, minor_str, 10) catch continue;
++
++            if (major > best_match_version.major or (major == best_match_version.major and minor >= best_match_version.minor)) {
++                best_match_version.major = major;
++                best_match_version.minor = minor;
++                best_match = try arena.dupe(u8, entry.name);
++            }
++        }
++
++        if (best_match) |found| {
++            const test_path: Path = .{
++                .root_dir = lib_directory,
++                .sub_path = found,
++            };
++            try checked_paths.print(gpa, "\n  {f}", .{test_path});
++            switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, target, .{
++                .path = test_path,
++                .query = name_query.query,
++            }, link_mode, color)) {
++                .no_match => {},
++                .ok => return .ok,
++            }
++        }
++    }
++
+     return .no_match;
+ }
+
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.