Skip to content

Commit 8d4457f

Browse files
committed
std.Build: allow adding search methods with function and command option
This allows more granularity when adding search paths, complementing `--search-prefix` for more specific use-cases. ## Multi-arch library layout On systems with multilib support (e.g. Gentoo with 32-bit and 64-bit libraries in `/usr/lib` and `/usr/lib64` respectively), using `--search-prefix` option can cause incorrect library selected. For example, if I want to link system `libcurl` library when building Zig program for non-native or explicit native-compatible target, I can try using `--search-prefix /usr`, which will automatically add: * `/usr/bin` as search path for binaries (fine), * `/usr/lib` as search path for libraries (incorrect), * `/usr/include` as search path for includes (fine), In my case it will find 32-bit libraries for 64-bit target, causing linker errors: ```console $ zig build -Dtarget=x86_64-linux-gnu.2.35 --search-prefix /usr error: ld.lld: /usr/lib/libcurl.so is incompatible with elf_x86_64 ``` New `--search-paths` option allows to add search paths explicitly, using passed ZON file as a specification. You can repeat multiple times to add many different search paths if needed. ```zig .{ .binaries = "/usr/bin" .libraries = "/usr/lib64", .includes = "/usr/include", } ``` ## Other layouts (GCC or Android NDK etc.) Another use-case is when binaries, libraries and includes are distributed across different hierarchies. Suppose I want to correctly find `gcc` binary and link `gccjit` library for my project: ```zig .{ .binaries = "/usr/x86_64-pc-linux-gnu/gcc-bin/14/", .libraries = "/usr/lib/gcc/x86_64-pc-linux-gnu/14", .includes = "/usr/lib/gcc/x86_64-pc-linux-gnu/14/include/", } ``` Or use Android NDK: ```zig .{ // you can omit fields or set `null` if you don't want to search them. // .binaries = null, .libraries = "/path_to_ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/35", .includes = "/path_to_ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include", } ``` ```zig .{ .libraries = "/path_to_ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android", } ``` Signed-off-by: Eric Joldasov <bratishkaerik@landless-city.net>
1 parent ba1be1d commit 8d4457f

File tree

2 files changed

+66
-2
lines changed

2 files changed

+66
-2
lines changed

lib/compiler/build_runner.zig

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,13 @@ pub fn main() !void {
176176
} else if (mem.eql(u8, arg, "--search-prefix")) {
177177
const search_prefix = nextArgOrFatal(args, &arg_idx);
178178
builder.addSearchPrefix(search_prefix);
179+
} else if (mem.eql(u8, arg, "--search-paths")) {
180+
const search_paths_spec = nextArgOrFatal(args, &arg_idx);
181+
182+
const search_paths = parseZonFile(std.Build.SearchMethod.Paths, builder, search_paths_spec) catch |err|
183+
return fatal("Unable to parse ZON file '{s}': {s}", .{ search_paths_spec, @errorName(err) });
184+
185+
builder.addSearchMethod(.{ .paths = search_paths });
179186
} else if (mem.eql(u8, arg, "--libc")) {
180187
builder.libc_file = nextArgOrFatal(args, &arg_idx);
181188
} else if (mem.eql(u8, arg, "--color")) {
@@ -1341,6 +1348,7 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
13411348
\\ --search-prefix [path] Add a path to look for binaries, libraries, headers
13421349
\\ --sysroot [path] Set the system root directory (usually /)
13431350
\\ --libc [file] Provide a file which specifies libc paths
1351+
\\ --search-paths [file] Provide a file which specifies search paths
13441352
\\
13451353
\\ --system [pkgdir] Disable package fetching; enable all integrations
13461354
\\ -fsys=[name] Enable a system integration
@@ -1542,3 +1550,55 @@ fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void {
15421550
};
15431551
}
15441552
}
1553+
1554+
/// Parse ZON file where all fields are paths relative to
1555+
/// current working directory.
1556+
fn parseZonFile(comptime T: type, b: *std.Build, path: []const u8) !T {
1557+
const spec_file = try std.fs.cwd().openFile(path, .{});
1558+
defer spec_file.close();
1559+
1560+
const gpa = b.allocator;
1561+
1562+
const spec = try std.zig.readSourceFileToEndAlloc(gpa, spec_file, null);
1563+
defer gpa.free(spec);
1564+
1565+
var ast: std.zig.Ast = try .parse(gpa, spec, .zon);
1566+
defer ast.deinit(gpa);
1567+
1568+
const zoir = try std.zig.ZonGen.generate(gpa, ast, .{});
1569+
defer zoir.deinit(gpa);
1570+
1571+
if (zoir.hasCompileErrors()) {
1572+
std.log.err("Can't parse ZON file '{s}: {d} errors", .{ path, zoir.compile_errors.len });
1573+
for (zoir.compile_errors, 1..) |compile_error, i| {
1574+
std.log.err("[{d}] {s}", .{ i, compile_error.msg.get(zoir) });
1575+
for (compile_error.getNotes(zoir)) |note| {
1576+
std.log.err("note: {s}", .{note.msg.get(zoir)});
1577+
}
1578+
}
1579+
return process.exit(1);
1580+
}
1581+
1582+
var result: T = .{};
1583+
const root_struct = switch (std.zig.Zoir.Node.Index.root.get(zoir)) {
1584+
.struct_literal => |struct_literal| struct_literal,
1585+
.empty_literal => return result,
1586+
else => return fatal("Can't parse ZON file '{s}': not a struct", .{path}),
1587+
};
1588+
1589+
for (root_struct.names, 0..) |name_i, val_i| {
1590+
const name = name_i.get(zoir);
1591+
const val = root_struct.vals.at(@intCast(val_i)).get(zoir);
1592+
1593+
inline for (@typeInfo(T).@"struct".fields) |field| {
1594+
if (std.mem.eql(u8, name, field.name)) {
1595+
const string = switch (val) {
1596+
.string_literal => |string_literal| string_literal,
1597+
else => return fatal("Can't parse field '{s}' in ZON file '{s}': not a string", .{ field.name, path }),
1598+
};
1599+
@field(result, field.name) = .{ .cwd_relative = b.dupePath(string) };
1600+
}
1601+
}
1602+
}
1603+
return result;
1604+
}

lib/std/Build.zig

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2133,9 +2133,13 @@ pub const SearchMethod = union(enum) {
21332133
};
21342134

21352135
pub fn addSearchPrefix(b: *Build, search_prefix: []const u8) void {
2136-
b.search_methods.append(b.allocator, .{
2136+
b.addSearchMethod(.{
21372137
.prefix = .{ .cwd_relative = b.dupePath(search_prefix) },
2138-
}) catch @panic("OOM");
2138+
});
2139+
}
2140+
2141+
pub fn addSearchMethod(b: *Build, search_method: SearchMethod) void {
2142+
b.search_methods.append(b.allocator, search_method) catch @panic("OOM");
21392143
}
21402144

21412145
pub fn getInstallPath(b: *Build, dir: InstallDir, dest_rel_path: []const u8) []const u8 {

0 commit comments

Comments
 (0)