Skip to content

Commit f99b753

Browse files
authored
Merge pull request #15061 from ziglang/fix-15036
build: fix adding rpaths on darwin, improve CheckObjectStep to allow matching FileSource paths
2 parents 3aa0a7e + 145f93b commit f99b753

File tree

3 files changed

+72
-32
lines changed

3 files changed

+72
-32
lines changed

lib/std/Build/CheckObjectStep.zig

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ pub fn runAndCompare(self: *CheckObjectStep) *std.Build.RunStep {
5858
return run;
5959
}
6060

61+
const SearchPhrase = struct {
62+
string: []const u8,
63+
file_source: ?std.Build.FileSource = null,
64+
65+
fn resolve(phrase: SearchPhrase, b: *std.Build, step: *Step) []const u8 {
66+
const file_source = phrase.file_source orelse return phrase.string;
67+
return b.fmt("{s} {s}", .{ phrase.string, file_source.getPath2(b, step) });
68+
}
69+
};
70+
6171
/// There two types of actions currently suported:
6272
/// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}`
6373
/// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature
@@ -72,7 +82,7 @@ pub fn runAndCompare(self: *CheckObjectStep) *std.Build.RunStep {
7282
/// they could then be added with this simple program `vmaddr entryoff +`.
7383
const Action = struct {
7484
tag: enum { match, not_present, compute_cmp },
75-
phrase: []const u8,
85+
phrase: SearchPhrase,
7686
expected: ?ComputeCompareExpected = null,
7787

7888
/// Will return true if the `phrase` was found in the `haystack`.
@@ -83,12 +93,18 @@ const Action = struct {
8393
/// and save under `vmaddr` global name (see `global_vars` param)
8494
/// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib`
8595
/// in that order with other letters in between
86-
fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool {
96+
fn match(
97+
act: Action,
98+
b: *std.Build,
99+
step: *Step,
100+
haystack: []const u8,
101+
global_vars: anytype,
102+
) !bool {
87103
assert(act.tag == .match or act.tag == .not_present);
88-
104+
const phrase = act.phrase.resolve(b, step);
89105
var candidate_var: ?struct { name: []const u8, value: u64 } = null;
90106
var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");
91-
var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " ");
107+
var needle_it = mem.tokenize(u8, mem.trim(u8, phrase, " "), " ");
92108

93109
while (needle_it.next()) |needle_tok| {
94110
const hay_tok = hay_it.next() orelse return false;
@@ -133,12 +149,13 @@ const Action = struct {
133149
/// Will return true if the `phrase` is correctly parsed into an RPN program and
134150
/// its reduced, computed value compares using `op` with the expected value, either
135151
/// a literal or another extracted variable.
136-
fn computeCmp(act: Action, step: *Step, global_vars: anytype) !bool {
152+
fn computeCmp(act: Action, b: *std.Build, step: *Step, global_vars: anytype) !bool {
137153
const gpa = step.owner.allocator;
154+
const phrase = act.phrase.resolve(b, step);
138155
var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa);
139156
var values = std.ArrayList(u64).init(gpa);
140157

141-
var it = mem.tokenize(u8, act.phrase, " ");
158+
var it = mem.tokenize(u8, phrase, " ");
142159
while (it.next()) |next| {
143160
if (mem.eql(u8, next, "+")) {
144161
try op_stack.append(.add);
@@ -225,43 +242,41 @@ const ComputeCompareExpected = struct {
225242
};
226243

227244
const Check = struct {
228-
builder: *std.Build,
229245
actions: std.ArrayList(Action),
230246

231-
fn create(b: *std.Build) Check {
247+
fn create(allocator: Allocator) Check {
232248
return .{
233-
.builder = b,
234-
.actions = std.ArrayList(Action).init(b.allocator),
249+
.actions = std.ArrayList(Action).init(allocator),
235250
};
236251
}
237252

238-
fn match(self: *Check, phrase: []const u8) void {
253+
fn match(self: *Check, phrase: SearchPhrase) void {
239254
self.actions.append(.{
240255
.tag = .match,
241-
.phrase = self.builder.dupe(phrase),
256+
.phrase = phrase,
242257
}) catch @panic("OOM");
243258
}
244259

245-
fn notPresent(self: *Check, phrase: []const u8) void {
260+
fn notPresent(self: *Check, phrase: SearchPhrase) void {
246261
self.actions.append(.{
247262
.tag = .not_present,
248-
.phrase = self.builder.dupe(phrase),
263+
.phrase = phrase,
249264
}) catch @panic("OOM");
250265
}
251266

252-
fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void {
267+
fn computeCmp(self: *Check, phrase: SearchPhrase, expected: ComputeCompareExpected) void {
253268
self.actions.append(.{
254269
.tag = .compute_cmp,
255-
.phrase = self.builder.dupe(phrase),
270+
.phrase = phrase,
256271
.expected = expected,
257272
}) catch @panic("OOM");
258273
}
259274
};
260275

261276
/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.
262277
pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {
263-
var new_check = Check.create(self.step.owner);
264-
new_check.match(phrase);
278+
var new_check = Check.create(self.step.owner.allocator);
279+
new_check.match(.{ .string = self.step.owner.dupe(phrase) });
265280
self.checks.append(new_check) catch @panic("OOM");
266281
}
267282

@@ -270,7 +285,19 @@ pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {
270285
pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void {
271286
assert(self.checks.items.len > 0);
272287
const last = &self.checks.items[self.checks.items.len - 1];
273-
last.match(phrase);
288+
last.match(.{ .string = self.step.owner.dupe(phrase) });
289+
}
290+
291+
/// Like `checkNext()` but takes an additional argument `FileSource` which will be
292+
/// resolved to a full search query in `make()`.
293+
pub fn checkNextFileSource(
294+
self: *CheckObjectStep,
295+
phrase: []const u8,
296+
file_source: std.Build.FileSource,
297+
) void {
298+
assert(self.checks.items.len > 0);
299+
const last = &self.checks.items[self.checks.items.len - 1];
300+
last.match(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source });
274301
}
275302

276303
/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`
@@ -279,7 +306,7 @@ pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void {
279306
pub fn checkNotPresent(self: *CheckObjectStep, phrase: []const u8) void {
280307
assert(self.checks.items.len > 0);
281308
const last = &self.checks.items[self.checks.items.len - 1];
282-
last.notPresent(phrase);
309+
last.notPresent(.{ .string = self.step.owner.dupe(phrase) });
283310
}
284311

285312
/// Creates a new check checking specifically symbol table parsed and dumped from the object
@@ -302,8 +329,8 @@ pub fn checkComputeCompare(
302329
program: []const u8,
303330
expected: ComputeCompareExpected,
304331
) void {
305-
var new_check = Check.create(self.step.owner);
306-
new_check.computeCmp(program, expected);
332+
var new_check = Check.create(self.step.owner.allocator);
333+
new_check.computeCmp(.{ .string = self.step.owner.dupe(program) }, expected);
307334
self.checks.append(new_check) catch @panic("OOM");
308335
}
309336

@@ -343,7 +370,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
343370
switch (act.tag) {
344371
.match => {
345372
while (it.next()) |line| {
346-
if (try act.match(line, &vars)) break;
373+
if (try act.match(b, step, line, &vars)) break;
347374
} else {
348375
return step.fail(
349376
\\
@@ -352,25 +379,25 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
352379
\\========= but parsed file does not contain it: =======
353380
\\{s}
354381
\\======================================================
355-
, .{ act.phrase, output });
382+
, .{ act.phrase.resolve(b, step), output });
356383
}
357384
},
358385
.not_present => {
359386
while (it.next()) |line| {
360-
if (try act.match(line, &vars)) {
387+
if (try act.match(b, step, line, &vars)) {
361388
return step.fail(
362389
\\
363390
\\========= expected not to find: ===================
364391
\\{s}
365392
\\========= but parsed file does contain it: ========
366393
\\{s}
367394
\\===================================================
368-
, .{ act.phrase, output });
395+
, .{ act.phrase.resolve(b, step), output });
369396
}
370397
}
371398
},
372399
.compute_cmp => {
373-
const res = act.computeCmp(step, vars) catch |err| switch (err) {
400+
const res = act.computeCmp(b, step, vars) catch |err| switch (err) {
374401
error.UnknownVariable => {
375402
return step.fail(
376403
\\========= from parsed file: =====================
@@ -388,7 +415,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
388415
\\========= from parsed file: =======================
389416
\\{s}
390417
\\===================================================
391-
, .{ act.phrase, act.expected.?, output });
418+
, .{ act.phrase.resolve(b, step), act.expected.?, output });
392419
}
393420
},
394421
}

lib/std/Build/CompileStep.zig

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,6 +1725,22 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
17251725
try zig_args.ensureUnusedCapacity(2 * self.rpaths.items.len);
17261726
for (self.rpaths.items) |rpath| {
17271727
zig_args.appendAssumeCapacity("-rpath");
1728+
1729+
if (self.target_info.target.isDarwin()) switch (rpath) {
1730+
.path => |path| {
1731+
// On Darwin, we should not try to expand special runtime paths such as
1732+
// * @executable_path
1733+
// * @loader_path
1734+
if (mem.startsWith(u8, path, "@executable_path") or
1735+
mem.startsWith(u8, path, "@loader_path"))
1736+
{
1737+
zig_args.appendAssumeCapacity(path);
1738+
continue;
1739+
}
1740+
},
1741+
.generated => {},
1742+
};
1743+
17281744
zig_args.appendAssumeCapacity(rpath.getPath2(b, step));
17291745
}
17301746

test/link/macho/dylib/build.zig

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
5252
check_exe.checkNext("compatibility version 10000");
5353

5454
check_exe.checkStart("cmd RPATH");
55-
// TODO check this (perhaps with `checkNextFileSource(dylib.getOutputDirectorySource())`)
56-
//check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{
57-
// b.pathFromRoot("zig-out/lib"),
58-
//}) catch unreachable);
55+
check_exe.checkNextFileSource("path", dylib.getOutputDirectorySource());
5956

6057
const run = check_exe.runAndCompare();
6158
run.expectStdOutEqual("Hello world");

0 commit comments

Comments
 (0)