diff --git a/test/link/macho.zig b/test/link/macho.zig index e5d04d9733..27ae09a22e 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -53,6 +53,8 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { macho_step.dependOn(testHeaderpad(b, .{ .target = b.host })); macho_step.dependOn(testNeededFramework(b, .{ .target = b.host })); macho_step.dependOn(testObjc(b, .{ .target = b.host })); + macho_step.dependOn(testObjcStubs(b, .{ .target = b.host })); + macho_step.dependOn(testObjcStubs2(b, .{ .target = b.host })); macho_step.dependOn(testWeakFramework(b, .{ .target = b.host })); } } @@ -289,6 +291,7 @@ fn testEntryPointArchive(b: *Build, opts: Options) *Step { exe.root_module.addLibraryPath(lib.getEmittedBinDirectory()); const run = addRunArtifact(exe); + run.expectExitCode(0); test_step.dependOn(&run.step); } @@ -299,6 +302,7 @@ fn testEntryPointArchive(b: *Build, opts: Options) *Step { exe.link_gc_sections = true; const run = addRunArtifact(exe); + run.expectExitCode(0); test_step.dependOn(&run.step); } @@ -769,6 +773,7 @@ fn testNoDeadStrip(b: *Build, opts: Options) *Step { test_step.dependOn(&check.step); const run = addRunArtifact(exe); + run.expectExitCode(0); test_step.dependOn(&run.step); return test_step; @@ -859,6 +864,155 @@ fn testObjc(b: *Build, opts: Options) *Step { return test_step; } +fn testObjcStubs(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "macho-objc-stubs", opts); + + const exe = addExecutable(b, opts, .{ + .name = "main", + .objc_source_bytes = + \\@import Foundation; + \\@interface Foo : NSObject + \\@property (nonatomic, assign) NSString* name; + \\@end + \\@implementation Foo + \\- (void)bar { + \\ printf("%s", [self.name UTF8String]); + \\} + \\@end + \\int main() { + \\ Foo *foo = [[Foo alloc] init]; + \\ foo.name = @"Foo"; + \\ [foo bar]; + \\ return 0; + \\} + , + .objc_source_flags = &.{ "-fmodules", "-fobjc-msgsend-selector-stubs" }, + }); + exe.root_module.linkFramework("Foundation", .{}); + + const run = addRunArtifact(exe); + run.expectStdOutEqual("Foo"); + test_step.dependOn(&run.step); + + const check = exe.checkObject(); + check.checkInHeaders(); + check.checkExact("sectname __objc_stubs"); + check.checkInHeaders(); + check.checkExact("sectname __objc_methname"); + check.checkInHeaders(); + check.checkExact("sectname __objc_selrefs"); + check.checkInSymtab(); + check.checkContains("(__TEXT,__objc_stubs) (was private external) _objc_msgSend$bar"); + check.checkInSymtab(); + check.checkContains("(__TEXT,__objc_stubs) (was private external) _objc_msgSend$name"); + check.checkInSymtab(); + check.checkContains("(__TEXT,__objc_stubs) (was private external) _objc_msgSend$setName"); + test_step.dependOn(&check.step); + + return test_step; +} + +fn testObjcStubs2(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "macho-objc-stubs-2", opts); + + const all_h = all_h: { + const wf = WriteFile.create(b); + break :all_h wf.add("all.h", + \\#import + \\ + \\@interface Foo : NSObject + \\@property (nonatomic, assign) NSString* name; + \\- (void) foo; + \\@end + \\@interface Bar : NSObject + \\@property (nonatomic, assign) NSString* name; + \\- (void) bar; + \\- (void) foobar: (Foo*) foo; + \\@end + ); + }; + + const foo_o = addObject(b, opts, .{ + .name = "foo", + .objc_source_bytes = + \\#import + \\#import "all.h" + \\@implementation Foo + \\- (void)foo { + \\ printf("%s", [self.name UTF8String]); + \\} + \\@end + , + .objc_source_flags = &.{"-fobjc-msgsend-selector-stubs"}, + }); + foo_o.root_module.addIncludePath(all_h.dirname()); + + const bar_o = addObject(b, opts, .{ + .name = "bar", + .objc_source_bytes = + \\#import + \\#import "all.h" + \\@implementation Bar + \\- (void)bar { + \\ printf("%s", [self.name UTF8String]); + \\} + \\- (void)foobar: (Foo*) foo { + \\ printf("%s%s", [foo.name UTF8String], [self.name UTF8String]); + \\} + \\@end + , + .objc_source_flags = &.{"-fobjc-msgsend-selector-stubs"}, + }); + bar_o.root_module.addIncludePath(all_h.dirname()); + + const main_o = addObject(b, opts, .{ + .name = "main", + .objc_source_bytes = + \\#import + \\#import "all.h" + \\int main() { + \\ Foo *foo = [[Foo alloc] init]; + \\ foo.name = @"Foo"; + \\ Bar *bar = [[Bar alloc] init]; + \\ bar.name = @"Bar"; + \\ [foo foo]; + \\ [bar bar]; + \\ [bar foobar:foo]; + \\ return 0; + \\} + , + .objc_source_flags = &.{"-fobjc-msgsend-selector-stubs"}, + }); + main_o.root_module.addIncludePath(all_h.dirname()); + + const exe = addExecutable(b, opts, .{ .name = "main" }); + exe.addObject(main_o); + exe.addObject(foo_o); + exe.addObject(bar_o); + exe.root_module.linkFramework("Foundation", .{}); + + const run = addRunArtifact(exe); + run.expectStdOutEqual("FooBarFooBar"); + test_step.dependOn(&run.step); + + const check = exe.checkObject(); + check.checkInHeaders(); + check.checkExact("sectname __objc_stubs"); + check.checkInHeaders(); + check.checkExact("sectname __objc_methname"); + check.checkInHeaders(); + check.checkExact("sectname __objc_selrefs"); + check.checkInSymtab(); + check.checkContains("(__TEXT,__objc_stubs) (was private external) _objc_msgSend$foo"); + check.checkInSymtab(); + check.checkContains("(__TEXT,__objc_stubs) (was private external) _objc_msgSend$bar"); + check.checkInSymtab(); + check.checkContains("(__TEXT,__objc_stubs) (was private external) _objc_msgSend$foobar"); + test_step.dependOn(&check.step); + + return test_step; +} + fn testRelocatable(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "macho-relocatable", opts); @@ -1647,3 +1801,4 @@ const BuildOptions = link.BuildOptions; const Compile = Step.Compile; const Options = link.Options; const Step = Build.Step; +const WriteFile = Step.WriteFile;