Skip to content

Commit

Permalink
std.Build: allow adding exact search prefixes with function and comma…
Browse files Browse the repository at this point in the history
…nd option

This allows more granurality when adding search pathes for cross-compiling.
By default old (all variant) system added `bin`, `include` and `lib`
subdirectories from `search-prefix` argument path when cross-compiling,
but on some distros it's not correct and can lead to some errors.

For example, on my Gentoo system I have libcurl installed as both
32-bit and 64-bit versions, which are located in `/usr/lib` and `/usr/lib64`
 directories respectively. If I use plain `--search-prefix`, it fails
because it tries to add 32-bit libraries:
```console
$ zig build -Dtarget=x86_64-linux-gnu.2.35 --search-prefix /usr/
install
└─ install true_or_false
   └─ zig build-exe true_or_false Debug x86_64-linux.6.11.7-gnu.2.40 3 errors
error: ld.lld: /usr/lib/libcurl.so is incompatible with elf_x86_64
error: ld.lld: /usr/lib/libssl.so is incompatible with elf_x86_64
error: ld.lld: /usr/lib/libcrypto.so is incompatible with elf_x86_64
```

Another example is `libgccjit` library, which shared objects are located
(at least on my system) in `/usr/lib/gcc/x86_64-pc-linux-gnu/14/`, and
include files in include subdir there. One solution I used recently
was to create temporary directory and symlink files so that they have
names expected by Zig, but it's not the best solution.

With this PR you can now supply file which will instruct build system
to add pathes written there as-is, without trying to guess subdirs there.
Something like:
```zig
.{
    .libraries = "/usr/lib64",
}
```
or
```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/",
}
```

can be saved in some file like paths.zon and then passed using
`--search-prefix-exact paths.zon`. This also helps to use this config
on a system (distro) level, without messing with build.zig.

Signed-off-by: Eric Joldasov <[email protected]>
  • Loading branch information
BratishkaErik committed Jan 20, 2025
1 parent 5782905 commit 47c69de
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 0 deletions.
60 changes: 60 additions & 0 deletions lib/compiler/build_runner.zig
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,13 @@ pub fn main() !void {
} else if (mem.eql(u8, arg, "--search-prefix")) {
const search_prefix = nextArgOrFatal(args, &arg_idx);
builder.addSearchPrefix(search_prefix);
} else if (mem.eql(u8, arg, "--search-prefix-exact")) {
const search_prefix_spec_path = nextArgOrFatal(args, &arg_idx);

const search_prefix = parseZonFile(std.Build.SearchPrefix.Exact, builder, search_prefix_spec_path) catch |err|
return fatal("Unable to parse ZON file '{s}': {s}", .{ search_prefix_spec_path, @errorName(err) });

builder.addSearchPrefixExact(search_prefix);
} else if (mem.eql(u8, arg, "--libc")) {
builder.libc_file = nextArgOrFatal(args, &arg_idx);
} else if (mem.eql(u8, arg, "--color")) {
Expand Down Expand Up @@ -1317,6 +1324,7 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
\\ --search-prefix [path] Add a path to look for binaries, libraries, headers
\\ --sysroot [path] Set the system root directory (usually /)
\\ --libc [file] Provide a file which specifies libc paths
\\ --search-prefix-exact [file] Provide a file which specifies exact search paths
\\
\\ --system [pkgdir] Disable package fetching; enable all integrations
\\ -fsys=[name] Enable a system integration
Expand Down Expand Up @@ -1518,3 +1526,55 @@ fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void {
};
}
}

/// Parse ZON file where all fields are paths relative to
/// current working directory.
fn parseZonFile(comptime T: type, b: *std.Build, path: []const u8) !T {
const spec_file = try std.fs.cwd().openFile(path, .{});
defer spec_file.close();

const gpa = b.allocator;

const spec = try std.zig.readSourceFileToEndAlloc(gpa, spec_file, null);
defer gpa.free(spec);

var ast: std.zig.Ast = try .parse(gpa, spec, .zon);
defer ast.deinit(gpa);

const zoir = try std.zig.ZonGen.generate(gpa, ast);
defer zoir.deinit(gpa);

if (zoir.hasCompileErrors()) {
std.log.err("Can't parse ZON file '{s}: {d} errors", .{ path, zoir.compile_errors.len });
for (zoir.compile_errors, 1..) |compile_error, i| {
std.log.err("[{d}] {s}", .{ i, compile_error.msg.get(zoir) });
for (compile_error.getNotes(zoir)) |note| {
std.log.err("note: {s}", .{note.msg.get(zoir)});
}
}
return process.exit(1);
}

var result: T = .{};
const root_struct = switch (std.zig.Zoir.Node.Index.root.get(zoir)) {
.struct_literal => |struct_literal| struct_literal,
.empty_literal => return result,
else => return fatal("Can't parse ZON file '{s}': not a struct", .{path}),
};

for (root_struct.names, 0..) |name_i, val_i| {
const name = name_i.get(zoir);
const val = root_struct.vals.at(@intCast(val_i)).get(zoir);

inline for (@typeInfo(T).@"struct".fields) |field| {
if (std.mem.eql(u8, name, field.name)) {
const string = switch (val) {
.string_literal => |string_literal| string_literal,
else => return fatal("Can't parse field '{s}' in ZON file '{s}': not a string", .{ field.name, path }),
};
@field(result, field.name) = .{ .cwd_relative = b.dupePath(string) };
}
}
}
return result;
}
4 changes: 4 additions & 0 deletions lib/std/Build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2089,6 +2089,10 @@ pub fn addSearchPrefix(b: *Build, search_prefix: []const u8) void {
}) catch @panic("OOM");
}

pub fn addSearchPrefixExact(b: *Build, search_prefix_exact: SearchPrefix.Exact) void {
b.search_prefixes.append(b.allocator, .{ .exact = search_prefix_exact }) catch @panic("OOM");
}

pub fn getInstallPath(b: *Build, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix
const base_dir = switch (dir) {
Expand Down

0 comments on commit 47c69de

Please sign in to comment.