6

What should be the proper way to use classes in a static library in a private framework, an app and an extension? My sample project can be found here https://github.com/keithyipkw/framework

In the second commit, the SDK was linked with the .a. running the app created the error

Ld /Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Products/Debug-iphonesimulator/FrameworkApp.app/FrameworkApp normal x86_64
    cd /Users/keithyip/Documents/Workspace/FrameworkApp
    export IPHONEOS_DEPLOYMENT_TARGET=8.4
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -arch x86_64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.4.sdk -L/Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Products/Debug-iphonesimulator -F/Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Products/Debug-iphonesimulator -filelist /Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Intermediates/FrameworkApp.build/Debug-iphonesimulator/FrameworkApp.build/Objects-normal/x86_64/FrameworkApp.LinkFileList -Xlinker -rpath -Xlinker @executable_path/Frameworks -Xlinker -objc_abi_version -Xlinker 2 -fobjc-arc -fobjc-link-runtime -Xlinker -no_implicit_dylibs -mios-simulator-version-min=8.4 /Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Products/Debug-iphonesimulator/SDK.framework/SDK -Xlinker -dependency_info -Xlinker /Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Intermediates/FrameworkApp.build/Debug-iphonesimulator/FrameworkApp.build/Objects-normal/x86_64/FrameworkApp_dependency_info.dat -o /Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Products/Debug-iphonesimulator/FrameworkApp.app/FrameworkApp

Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_GAI", referenced from:
      objc-class-ref in AppDelegate.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

The symbol is global in the .a but local in the SDK

$ nm -a /Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Products/Debug-iphonesimulator/SDK.framework/SDK | grep '_OBJC_CLASS_$_GAI'
00000000000d94e0 s _OBJC_CLASS_$_GAI

In the third commit, I added the .a to the app target. The app ran but with warnings

objc[3743]: Class GAI is implemented in both /Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Products/Debug-iphonesimulator/SDK.framework/SDK and /Users/keithyip/Library/Developer/CoreSimulator/Devices/752A7B8E-405E-4403-BDD8-A168613774B1/data/Containers/Bundle/Application/D16B5121-2DA9-452B-9574-95B35AE3E197/FrameworkApp.app/FrameworkApp. One of the two will be used. Which one is undefined.

I checked the SDK and app binary according to the file paths in the warning

$ nm -a /Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Products/Debug-iphonesimulator/SDK.framework/SDK | grep '_OBJC_CLASS_$_GAI'
00000000000d94e0 s _OBJC_CLASS_$_GAI

$ nm -a /Users/keithyip/Library/Developer/CoreSimulator/Devices/752A7B8E-405E-4403-BDD8-A168613774B1/data/Containers/Bundle/Application/D16B5121-2DA9-452B-9574-95B35AE3E197/FrameworkApp.app/FrameworkApp | grep '_OBJC_CLASS_$_GAI'
0000000100032c88 s _OBJC_CLASS_$_GAI

Digging deeper with nm -m, libGoogleAnalyticsServices.a is different from other static libraries. Linking to other libraries in a dynamic library is problem free.

0000000000002b20 (__DATA,__objc_data) private external _OBJC_CLASS_$_GAI

It seems to be impossible to keep the symbols public

clang: error: invalid argument '-keep_private_externs' not allowed with '-dynamiclib'
keithyip
  • 985
  • 7
  • 21
  • I take it you have libGoogleAnalyticsServices.a in your project in the first case which seems to contain the class code. Your SDK only has the headers? Or are you linking that into your static library? Would be worth showing the full linker command that failed from the build for your first case. – Rory McKinnel Sep 11 '15 at 22:28
  • The sample SDK contains SDKObject which use a class in GA. My app also need to use the same class directly. I cannot make it build unless I add the .a to both the SDK and app. – keithyip Sep 12 '15 at 09:15

3 Answers3

4

Normally you can simply link a static library to a dynamic library. However in this case the symbols in the .a are private external (visibility=hidden), it is impossible to include libGoogleAnalyticsServices.a to a dynamic library and use the symbols outside the dynamic library on iOS. The options to solve it as this moment are

  1. Change the dynamic library to a static library
  2. Refactor the code to remove GA from the dynamic library
  3. Create wrapper classes for GA so the callees do not need to link to GA

If someone run into a similar situation in the future, other options are

  1. Ask the vendor to expose the symbols
  2. Check if there is any "objcopy --globalize-symbols" equivalent for iOS. The closest thing is objconv as I write this. It supports OS X but not iOS. An objcopy equivalent for Mac / iPhone?
Community
  • 1
  • 1
keithyip
  • 985
  • 7
  • 21
2

Took your project and tried a number of things. The only thing which looks like it would work would have been to use the -flat_namespace compile option. By default everything builds with a two-level namespace.

If you use the -flat_namespace option the linker will let you use -undefined suppress which prevents error on undefined symbols. The result would be that they would all fully resolve when the final link is performed and you link in the google.a with the app build.

Unfortunately, the google library is not build flat, so this can not work unless you could get a flat build.

The one thing that definitely does work is to change your SDK target to a static target. Taking what you posted and doing that change alone compiles and runs with no errors. So that seems your best bet or avoid the issue by other means as you noted in your comments.

Rory McKinnel
  • 7,936
  • 2
  • 17
  • 28
  • How did you make the app run with -undefined? I have tried but the app crashed because of missing symbols. – keithyip Sep 13 '15 at 10:25
  • It would not let me build because the Google library was not compatible. I set the -flat_space option on the app and SDK and -undefined suppress on the SDK only and only linked Google into the app, but it complained so never got as far as running. Only succes was setting SDK to static which worked first time and no errors. – Rory McKinnel Sep 13 '15 at 11:24
0

I was hitting a similar issue, except on OS X: I was creating a framework with a dylib inside, and linking in a static library. I wanted to make public symbols on the static library available for apps using the framework. Even though I had set correct visibility attributes on the static library classes/methods I wanted to export, these symbols were stripped out of the final library.

One workaround was to write a bit of code in the dynamic library to reference these symbols in the static lib; this wound up having the linker not strip the export symbols from the static lib.

The ultimate solution ended up being building the static library as a relocatable object file; the resulting dynamic library had all of the export symbols from the static lib I wanted, and the bit of reference code in the dynamic lib was not required.

JasonZ
  • 494
  • 4
  • 13
  • I had a similar issue while trying to wrap a static library to a private cocoapod. The solution was to use `-all_load` in other linker flags, this forces the linker to load all members of static archive libraries. – David Aug 14 '18 at 16:02