Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OCMock 3.0.2 linker error with .mm test file

I am using OCMock 3.0.2, which I've installed through cocoapods for my test target:

platform :ios, '7.0'
xcodeproj 'myProject.xcodeproj'

target :myTestTarget do
  pod 'OCMock', '~> 3.0.2'
end

link_with "myTestTarget"

In my test file (myTest.mm), I've included OCMock and want to try out the new verify-in-place strategy, like so:

- (void) test_myTest
{
    MyObject *obj = [MyObject new];
    id robotMock = OCMPartialMock(obj);

    [obj testMethod];

    // some asserts
    OCMVerify([obj _internalMethodToBeCalled]);
}

So far seems normal. However, when I tried to run this specific test case, I am getting linker error:

Undefined symbols for architecture i386:
  "OCMMakeLocation(objc_object*, char const*, int)", referenced from:
      -[MyTests test_myTest] in MyTests.o
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I verified that OCMock was pulled in correctly and OCMLocation.h/m files are referenced too. I see that OCMMakeLocation seems to be an extern function but the .m file is there as a dependent project in my pods build target, but somehow it is not being linked. I have to make my test to be .mm since I am including some c++ files. Why would this be the problem for OCMock?

like image 949
Kevin Liang Avatar asked Feb 13 '23 10:02

Kevin Liang


2 Answers

OCMMakeLocation is declared like this

OCMLocation.h:

extern OCMLocation *OCMMakeLocation(id testCase, const char *file, int line);

OCMLocation.m:

OCMLocation *OCMMakeLocation(id testCase, const char *fileCString, int line)
{
    return [OCMLocation locationWithTestCase:testCase file:[NSString stringWithUTF8String:fileCString] line:line];
}

It's a straight C function defined outside of the Objective-C interface. I'm not knowledgeable enough about what the complier is actually doing (maybe someone else can explain it better), but to the best of my understanding, this is what's going on: Your test file is an Objective-C++ file, so it's getting complied with C++ linkage, which does name mangling (see this about name mangling). However, OCMLocation is compiled as a Objective-C file, so it gets C linkage, not C++ linkage, so no name mangling. Because your test is complied with C++ linkage, it pulls in OCMock.h and assumes it's also a C++ header, so it assumes the result of compiling it's source will be the same, which it's not going to be.

Long story short, to fix this, all you need to do is tell the compiler that OCMock.h is a C header in your test file:

#ifdef __cplusplus
extern "C" {
#endif
#import <OCMock/OCMock.h>
#ifdef __cplusplus
}
#endif
like image 168
Jordan Avatar answered Feb 15 '23 11:02

Jordan


It looks like you are building OCMock from source, you're building it for x86_64 (64-bit) only, and then try to use the binary in a project that is i386 (32-bit). Using the pre-built binaries available from the downloads page should fix your issue because these binaries are fat, they contain i386 and x86_64.

It could be the case that Cocoapods is trying to do a Debug build, which has "only active args" set to true. I don't know enough about Cocoapods to say how to force it to do a Release build, which should create fat binaries.

like image 28
Erik Doernenburg Avatar answered Feb 15 '23 10:02

Erik Doernenburg