Prevent duplicate symbols when building static library with Cocoapods
Asked Answered
K

6

22

While I've seen many questions dealing with Cocoapods and static libraries, most of them seem to assume you'll eventually have a single workspace with your static library and end target app.

In my scenario, I am building a static library. More specifically, I'm hacking a MyLib.framework for users to consume. I'd really like to manage MyLib.framework's dependencies with Cocoapods, but it creates many pain points when consumers of my library also use Cocoapods.

For example, my library has an AFNetworking dependency which I manage with Cocoapods. When I build my library, it links in libPods.a which includes AFNetworking, as well as some "dummy" files/objects. If users of my framework also use Cocoapods to build their app, they'll see something like this:

duplicate symbol _OBJC_METACLASS_$_PodsDummy_Pods in:
    /Users/erikkerber/Dropbox/Projects/MillMain/MyLib.framework/BuddySDK(Pods-dummy.o)
    /Users/erikkerber/Library/Developer/Xcode/DerivedData/MillMain-fngfqhlslygksgcfuciznkpqfrbr/Build/Products/Debug-iphonesimulator/libPods.a(Pods-dummy.o)
duplicate symbol _OBJC_CLASS_$_PodsDummy_Pods in:
    /Users/erikkerber/Dropbox/Projects/MillMain/MyLib.framework/BuddySDK(Pods-dummy.o)
    /Users/erikkerber/Library/Developer/Xcode/DerivedData/MillMain-fngfqhlslygksgcfuciznkpqfrbr/Build/Products/Debug-iphonesimulator/libPods.a(Pods-dummy.o)
ld: 2 duplicate symbols for architecture i386

I imagine if they were to add an AFNetworking dependency, that they would also get duplicate symbols relating to AFNetworking.

I plan to eventually distribute MyLib with Cocoapods as well, but I also want to be able to distribute a MyLib.framework itself.

Is there any way to use Cocoapods with my library while making Cocoapods safe to any potential user?

Kenaz answered 21/1, 2014 at 4:37 Comment(2)
Are you planning to distribute the source code for your framework, or just binaries?Anton
@StefanFisk It will be open source, but for the simple case I would like to allow users to simply download MyLib.framework and only go into source if they want to customize it.Kenaz
C
16

In short, the only good way of distributing prebuilt libraries is by not including any of the dependencies, but leaving that up to the user. I.e. in your example, you would instruct your users how to also add AFNetworking to their project. The same basically applies regarding the dummy files.

Having said that, you could of course go for multiple prebuilt variants:

  • Include all dependencies.
  • Only include your lib’s source, leave dependencies up to the user.

We have been talking about creating a plugin to produce standalone static libraries, for the purpose that you want, but that’s as of yet not started and will probably take a little while longer. (Until someone/anyone has the time.)

As a workaround, you could use your Podfile’s post_install hook to remove the dummy files altogether. (these are only needed for non-source libs like Testflight anyways.) E.g. something like the following:

post_install do |installer|
  installer.project.targets.each do |target|
    source_files = target.source_build_phase.files
    dummy = source_files.find do |file|
      # TODO Fix this to the actual filename
      # puts "File: #{file.file_ref.name}"
      file.file_ref.name == 'TheDummyFile.m'
    end
    puts "Deleting source file #{dummy.inspect} from target #{target.inspect}."
    source_files.delete(dummy)
  end
end

This is untested code.

The post_install hook yields the CocoaPods installer object, from which you can get the Pods.xcodeproj targets, for which you can find documentation here. From there you can drill down and do anything you like to the project, which is saved to disk after running this hook.

Congregational answered 23/1, 2014 at 11:57 Comment(2)
Thank much, you confirmed my suspicion. I will contemplate your approaches, though I may go with the "just prefix the name all your dependency objects" approach and not use Cocoapods.Kenaz
Yeah that’s indeed the other route. You might want to look into using something like rentzsch.tumblr.com/post/40806448108/… so that your code can work easily be used in a CocoaPods environment without re-namespaced dependencies and in in your prebuilt lib with re-namespaced dependencies.Congregational
B
10

I had the same issue. I managed the dependencies of my library with cocoapods using the following podfile format:

platform :ios, '6.0'
pod 'AFNetworking'

This resulted in a Pods-dummy.o file in my .a file. If I then included this library in another project using the same podfile format, they both created a Pods-dummy.o symbol and results in a linker error. The solution is to use a different podfile format which results in a namespaced Pods-dummy symbol:

platform :ios, '6.0'
target "MyProject" do
    pod 'AFNetworking'
end

This results in a Pods-MyProject-dummy.o file, which won't cause duplicate symbols.

Note: if you switch your project over to using the new podfile format, make sure to unlink libPods.a from your project, since it will hang around as a broken link because the new pods library is named Pods-MyProject

Beckwith answered 29/4, 2014 at 16:59 Comment(2)
h/t for the bit about unlinking libPods.a! Any idea how to keep libPods.a being linked every time you run pod install?Spare
Static library dependencies are not linked into the static library product by default, so while @alloy's advice about not including dependencies is sound, I think much of his or her answer misses the issue. Removing the dummy object files does work, and is not a bad idea, but I think I think this solution is cleaner.Guthrey
C
5

If they are the same, you can merge them using:

libtool (libtool -o merged.a file1.a file2.a)

If this doesn't resolve the error, there is this option: http://atnan.com/blog/2012/01/12/avoiding-duplicate-symbol-errors-during-linking-by-removing-classes-from-static-libraries

Caloric answered 23/1, 2014 at 11:55 Comment(0)
S
3

With reference from http://guides.cocoapods.org/syntax/podfile.html#post_install & http://pdx.esri.com/blog/2013/12/13/namespacing-dependencies/

I use the 'Manually Rename All of the Symbols' approach. I was experiencing the duplicate symbol _OBJC_METACLASS_$_PodsDummy_Pods and so i added and edited the post_install in the Podfile to something like that to avoid the duplicate symbol

post_install do |installer_representation|
    installer_representation.pods_project.targets.each do |target|
        target.build_configurations.each do |config|
            config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited), PodsDummy_Pods=SomeOtherNamePodsDummy_Pods'
        end
    end
end
Scream answered 9/6, 2015 at 3:4 Comment(1)
Just replace project with pods_project. It is due to a recent update on Cocoapods.Vivisectionist
C
0

This problem came up for me when I had manually added some external code to my project AFNetworking, e.g., and then later added some pods that also included it.

Simply removing AFNetworking from my own Project (not the pod) eliminated the problem.

Chevaldefrise answered 25/1, 2015 at 21:2 Comment(0)
H
-1

add entries for each conflicting symbols to your Other C Flags eg "-DAFHTTPClient=AF2HTTPClient" Xcode Multiple Static Libraries and Duplicate Symbols

Hypnotherapy answered 1/4, 2015 at 17:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.