authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-24 07:22:05+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-03-24 07:22:05+01:00
logf99b75360db55413f4accf43a6f4161b14a5de9f
treebb57058b7ac0006f62813de35ce8b610587ee433
parent3aa0a7ecdf3f88d0d37e20c88bf57b69355573fe
parent145f93ba961fb9eea66a39b60e93c2aa5e26ee40
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #15061 from ziglang/fix-15036

build: fix adding rpaths on darwin, improve CheckObjectStep to allow matching FileSource paths

3 files changed, 72 insertions(+), 32 deletions(-)

lib/std/Build/CheckObjectStep.zig+55-28
...@@ -58,6 +58,16 @@ pub fn runAndCompare(self: *CheckObjectStep) *std.Build.RunStep {...@@ -58,6 +58,16 @@ pub fn runAndCompare(self: *CheckObjectStep) *std.Build.RunStep {
58 return run;58 return run;
59}59}
6060
61const 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
61/// There two types of actions currently suported:71/// There two types of actions currently suported:
62/// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}`72/// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}`
63/// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature73/// 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 {...@@ -72,7 +82,7 @@ pub fn runAndCompare(self: *CheckObjectStep) *std.Build.RunStep {
72/// they could then be added with this simple program `vmaddr entryoff +`.82/// they could then be added with this simple program `vmaddr entryoff +`.
73const Action = struct {83const Action = struct {
74 tag: enum { match, not_present, compute_cmp },84 tag: enum { match, not_present, compute_cmp },
75 phrase: []const u8,85 phrase: SearchPhrase,
76 expected: ?ComputeCompareExpected = null,86 expected: ?ComputeCompareExpected = null,
7787
78 /// Will return true if the `phrase` was found in the `haystack`.88 /// Will return true if the `phrase` was found in the `haystack`.
...@@ -83,12 +93,18 @@ const Action = struct {...@@ -83,12 +93,18 @@ const Action = struct {
83 /// and save under `vmaddr` global name (see `global_vars` param)93 /// and save under `vmaddr` global name (see `global_vars` param)
84 /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib`94 /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib`
85 /// in that order with other letters in between95 /// 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 {
87 assert(act.tag == .match or act.tag == .not_present);103 assert(act.tag == .match or act.tag == .not_present);
88104 const phrase = act.phrase.resolve(b, step);
89 var candidate_var: ?struct { name: []const u8, value: u64 } = null;105 var candidate_var: ?struct { name: []const u8, value: u64 } = null;
90 var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");106 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
93 while (needle_it.next()) |needle_tok| {109 while (needle_it.next()) |needle_tok| {
94 const hay_tok = hay_it.next() orelse return false;110 const hay_tok = hay_it.next() orelse return false;
...@@ -133,12 +149,13 @@ const Action = struct {...@@ -133,12 +149,13 @@ const Action = struct {
133 /// Will return true if the `phrase` is correctly parsed into an RPN program and149 /// Will return true if the `phrase` is correctly parsed into an RPN program and
134 /// its reduced, computed value compares using `op` with the expected value, either150 /// its reduced, computed value compares using `op` with the expected value, either
135 /// a literal or another extracted variable.151 /// 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 {
137 const gpa = step.owner.allocator;153 const gpa = step.owner.allocator;
154 const phrase = act.phrase.resolve(b, step);
138 var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa);155 var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa);
139 var values = std.ArrayList(u64).init(gpa);156 var values = std.ArrayList(u64).init(gpa);
140157
141 var it = mem.tokenize(u8, act.phrase, " ");158 var it = mem.tokenize(u8, phrase, " ");
142 while (it.next()) |next| {159 while (it.next()) |next| {
143 if (mem.eql(u8, next, "+")) {160 if (mem.eql(u8, next, "+")) {
144 try op_stack.append(.add);161 try op_stack.append(.add);
...@@ -225,34 +242,32 @@ const ComputeCompareExpected = struct {...@@ -225,34 +242,32 @@ const ComputeCompareExpected = struct {
225};242};
226243
227const Check = struct {244const Check = struct {
228 builder: *std.Build,
229 actions: std.ArrayList(Action),245 actions: std.ArrayList(Action),
230246
231 fn create(b: *std.Build) Check {247 fn create(allocator: Allocator) Check {
232 return .{248 return .{
233 .builder = b,249 .actions = std.ArrayList(Action).init(allocator),
234 .actions = std.ArrayList(Action).init(b.allocator),
235 };250 };
236 }251 }
237252
238 fn match(self: *Check, phrase: []const u8) void {253 fn match(self: *Check, phrase: SearchPhrase) void {
239 self.actions.append(.{254 self.actions.append(.{
240 .tag = .match,255 .tag = .match,
241 .phrase = self.builder.dupe(phrase),256 .phrase = phrase,
242 }) catch @panic("OOM");257 }) catch @panic("OOM");
243 }258 }
244259
245 fn notPresent(self: *Check, phrase: []const u8) void {260 fn notPresent(self: *Check, phrase: SearchPhrase) void {
246 self.actions.append(.{261 self.actions.append(.{
247 .tag = .not_present,262 .tag = .not_present,
248 .phrase = self.builder.dupe(phrase),263 .phrase = phrase,
249 }) catch @panic("OOM");264 }) catch @panic("OOM");
250 }265 }
251266
252 fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void {267 fn computeCmp(self: *Check, phrase: SearchPhrase, expected: ComputeCompareExpected) void {
253 self.actions.append(.{268 self.actions.append(.{
254 .tag = .compute_cmp,269 .tag = .compute_cmp,
255 .phrase = self.builder.dupe(phrase),270 .phrase = phrase,
256 .expected = expected,271 .expected = expected,
257 }) catch @panic("OOM");272 }) catch @panic("OOM");
258 }273 }
...@@ -260,8 +275,8 @@ const Check = struct {...@@ -260,8 +275,8 @@ const Check = struct {
260275
261/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.276/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.
262pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {277pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {
263 var new_check = Check.create(self.step.owner);278 var new_check = Check.create(self.step.owner.allocator);
264 new_check.match(phrase);279 new_check.match(.{ .string = self.step.owner.dupe(phrase) });
265 self.checks.append(new_check) catch @panic("OOM");280 self.checks.append(new_check) catch @panic("OOM");
266}281}
267282
...@@ -270,7 +285,19 @@ pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {...@@ -270,7 +285,19 @@ pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {
270pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void {285pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void {
271 assert(self.checks.items.len > 0);286 assert(self.checks.items.len > 0);
272 const last = &self.checks.items[self.checks.items.len - 1];287 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()`.
293pub 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 });
274}301}
275302
276/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`303/// 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 {...@@ -279,7 +306,7 @@ pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void {
279pub fn checkNotPresent(self: *CheckObjectStep, phrase: []const u8) void {306pub fn checkNotPresent(self: *CheckObjectStep, phrase: []const u8) void {
280 assert(self.checks.items.len > 0);307 assert(self.checks.items.len > 0);
281 const last = &self.checks.items[self.checks.items.len - 1];308 const last = &self.checks.items[self.checks.items.len - 1];
282 last.notPresent(phrase);309 last.notPresent(.{ .string = self.step.owner.dupe(phrase) });
283}310}
284311
285/// Creates a new check checking specifically symbol table parsed and dumped from the object312/// Creates a new check checking specifically symbol table parsed and dumped from the object
...@@ -302,8 +329,8 @@ pub fn checkComputeCompare(...@@ -302,8 +329,8 @@ pub fn checkComputeCompare(
302 program: []const u8,329 program: []const u8,
303 expected: ComputeCompareExpected,330 expected: ComputeCompareExpected,
304) void {331) void {
305 var new_check = Check.create(self.step.owner);332 var new_check = Check.create(self.step.owner.allocator);
306 new_check.computeCmp(program, expected);333 new_check.computeCmp(.{ .string = self.step.owner.dupe(program) }, expected);
307 self.checks.append(new_check) catch @panic("OOM");334 self.checks.append(new_check) catch @panic("OOM");
308}335}
309336
...@@ -343,7 +370,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -343,7 +370,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
343 switch (act.tag) {370 switch (act.tag) {
344 .match => {371 .match => {
345 while (it.next()) |line| {372 while (it.next()) |line| {
346 if (try act.match(line, &vars)) break;373 if (try act.match(b, step, line, &vars)) break;
347 } else {374 } else {
348 return step.fail(375 return step.fail(
349 \\376 \\
...@@ -352,12 +379,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -352,12 +379,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
352 \\========= but parsed file does not contain it: =======379 \\========= but parsed file does not contain it: =======
353 \\{s}380 \\{s}
354 \\======================================================381 \\======================================================
355 , .{ act.phrase, output });382 , .{ act.phrase.resolve(b, step), output });
356 }383 }
357 },384 },
358 .not_present => {385 .not_present => {
359 while (it.next()) |line| {386 while (it.next()) |line| {
360 if (try act.match(line, &vars)) {387 if (try act.match(b, step, line, &vars)) {
361 return step.fail(388 return step.fail(
362 \\389 \\
363 \\========= expected not to find: ===================390 \\========= expected not to find: ===================
...@@ -365,12 +392,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -365,12 +392,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
365 \\========= but parsed file does contain it: ========392 \\========= but parsed file does contain it: ========
366 \\{s}393 \\{s}
367 \\===================================================394 \\===================================================
368 , .{ act.phrase, output });395 , .{ act.phrase.resolve(b, step), output });
369 }396 }
370 }397 }
371 },398 },
372 .compute_cmp => {399 .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) {
374 error.UnknownVariable => {401 error.UnknownVariable => {
375 return step.fail(402 return step.fail(
376 \\========= from parsed file: =====================403 \\========= from parsed file: =====================
...@@ -388,7 +415,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -388,7 +415,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
388 \\========= from parsed file: =======================415 \\========= from parsed file: =======================
389 \\{s}416 \\{s}
390 \\===================================================417 \\===================================================
391 , .{ act.phrase, act.expected.?, output });418 , .{ act.phrase.resolve(b, step), act.expected.?, output });
392 }419 }
393 },420 },
394 }421 }
lib/std/Build/CompileStep.zig+16
...@@ -1725,6 +1725,22 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1725,6 +1725,22 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1725 try zig_args.ensureUnusedCapacity(2 * self.rpaths.items.len);1725 try zig_args.ensureUnusedCapacity(2 * self.rpaths.items.len);
1726 for (self.rpaths.items) |rpath| {1726 for (self.rpaths.items) |rpath| {
1727 zig_args.appendAssumeCapacity("-rpath");1727 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
1728 zig_args.appendAssumeCapacity(rpath.getPath2(b, step));1744 zig_args.appendAssumeCapacity(rpath.getPath2(b, step));
1729 }1745 }
17301746
test/link/macho/dylib/build.zig+1-4
...@@ -52,10 +52,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize...@@ -52,10 +52,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
52 check_exe.checkNext("compatibility version 10000");52 check_exe.checkNext("compatibility version 10000");
5353
54 check_exe.checkStart("cmd RPATH");54 check_exe.checkStart("cmd RPATH");
55 // TODO check this (perhaps with `checkNextFileSource(dylib.getOutputDirectorySource())`)55 check_exe.checkNextFileSource("path", dylib.getOutputDirectorySource());
56 //check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{
57 // b.pathFromRoot("zig-out/lib"),
58 //}) catch unreachable);
5956
60 const run = check_exe.runAndCompare();57 const run = check_exe.runAndCompare();
61 run.expectStdOutEqual("Hello world");58 run.expectStdOutEqual("Hello world");