Commit 28d08dd8a6

Jakub Konka <kubkon@jakubkonka.com>
2024-05-22 11:45:29
link/macho: test merging literals targeting ObjC
1 parent 8fc0c7d
Changed files (1)
test
test/link/macho.zig
@@ -83,6 +83,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
             macho_step.dependOn(testDeadStripDylibs(b, .{ .target = b.host }));
             macho_step.dependOn(testHeaderpad(b, .{ .target = b.host }));
             macho_step.dependOn(testLinkDirectlyCppTbd(b, .{ .target = b.host }));
+            macho_step.dependOn(testMergeLiteralsObjc(b, .{ .target = b.host }));
             macho_step.dependOn(testNeededFramework(b, .{ .target = b.host }));
             macho_step.dependOn(testObjc(b, .{ .target = b.host }));
             macho_step.dependOn(testObjcpp(b, .{ .target = b.host }));
@@ -1125,6 +1126,96 @@ fn testMergeLiterals2(b: *Build, opts: Options) *Step {
     return test_step;
 }
 
+fn testMergeLiteralsObjc(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "merge-literals-objc", opts);
+
+    const main_o = addObject(b, opts, .{ .name = "main", .objc_source_bytes = 
+    \\#import <Foundation/Foundation.h>;
+    \\
+    \\extern void foo();
+    \\
+    \\int main() {
+    \\  NSString *thing = @"aaa";
+    \\
+    \\  SEL sel = @selector(lowercaseString);
+    \\  NSString *lower = (([thing respondsToSelector:sel]) ? @"YES" : @"NO");
+    \\  NSLog (@"Responds to lowercaseString: %@", lower);
+    \\  if ([thing respondsToSelector:sel]) //(lower == @"YES")
+    \\      NSLog(@"lowercaseString is: %@", [thing lowercaseString]);
+    \\
+    \\  foo();
+    \\}
+    });
+
+    const a_o = addObject(b, opts, .{ .name = "a", .objc_source_bytes = 
+    \\#import <Foundation/Foundation.h>;
+    \\
+    \\void foo() {
+    \\  NSString *thing = @"aaa";
+    \\  SEL sel = @selector(lowercaseString);
+    \\  NSString *lower = (([thing respondsToSelector:sel]) ? @"YES" : @"NO");
+    \\  NSLog (@"Responds to lowercaseString in foo(): %@", lower);
+    \\  if ([thing respondsToSelector:sel]) //(lower == @"YES")
+    \\      NSLog(@"lowercaseString in foo() is: %@", [thing lowercaseString]);
+    \\  SEL sel2 = @selector(uppercaseString);
+    \\  NSString *upper = (([thing respondsToSelector:sel2]) ? @"YES" : @"NO");
+    \\  NSLog (@"Responds to uppercaseString in foo(): %@", upper);
+    \\  if ([thing respondsToSelector:sel2]) //(upper == @"YES")
+    \\      NSLog(@"uppercaseString in foo() is: %@", [thing uppercaseString]);
+    \\}
+    });
+
+    const runWithChecks = struct {
+        fn runWithChecks(step: *Step, exe: *Compile) void {
+            const builder = step.owner;
+            const run = addRunArtifact(exe);
+            run.addCheck(.{ .expect_stderr_match = builder.dupe("Responds to lowercaseString: YES") });
+            run.addCheck(.{ .expect_stderr_match = builder.dupe("lowercaseString is: aaa") });
+            run.addCheck(.{ .expect_stderr_match = builder.dupe("Responds to lowercaseString in foo(): YES") });
+            run.addCheck(.{ .expect_stderr_match = builder.dupe("lowercaseString in foo() is: aaa") });
+            run.addCheck(.{ .expect_stderr_match = builder.dupe("Responds to uppercaseString in foo(): YES") });
+            run.addCheck(.{ .expect_stderr_match = builder.dupe("uppercaseString in foo() is: AAA") });
+            step.dependOn(&run.step);
+
+            const check = exe.checkObject();
+            check.dumpSection("__TEXT,__objc_methname");
+            check.checkContains("lowercaseString\x00");
+            check.dumpSection("__TEXT,__objc_methname");
+            check.checkContains("uppercaseString\x00");
+            step.dependOn(&check.step);
+        }
+    }.runWithChecks;
+
+    {
+        const exe = addExecutable(b, opts, .{ .name = "main1" });
+        exe.addObject(main_o);
+        exe.addObject(a_o);
+        exe.root_module.linkFramework("Foundation", .{});
+        runWithChecks(test_step, exe);
+    }
+
+    {
+        const exe = addExecutable(b, opts, .{ .name = "main2" });
+        exe.addObject(a_o);
+        exe.addObject(main_o);
+        exe.root_module.linkFramework("Foundation", .{});
+        runWithChecks(test_step, exe);
+    }
+
+    {
+        const b_o = addObject(b, opts, .{ .name = "b" });
+        b_o.addObject(a_o);
+        b_o.addObject(main_o);
+
+        const exe = addExecutable(b, opts, .{ .name = "main3" });
+        exe.addObject(b_o);
+        exe.root_module.linkFramework("Foundation", .{});
+        runWithChecks(test_step, exe);
+    }
+
+    return test_step;
+}
+
 fn testMhExecuteHeader(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "mh-execute-header", opts);