Cocoapods: Duplicate Symbols when Podfile contain both root pods and `target`-specific pods

Created on 5 Jan 2014  Â·  27Comments  Â·  Source: CocoaPods/CocoaPods

CocoaPods version: 0.29.0 (but not sure if already present in earlier versions)
NB: Maybe related to #1674 and/or #1688?

Context

Let say we have the following Podfile:

pod 'Common'

target "MyApp" do
  pod 'Foo'
end

target "MyAppTests" do
  pod 'Bar'
end

:arrow_right: This leads to duplicate symbols for the files from the "Common" pod:
actualresult

Expected Result

We expect that CocoaPods would then create the following targets in Pods.xcodeproj:

  • Pods: "aggregate" target that simply links with libPods-Common.a
  • Pods-Common: target that builds the lib associated with the Common pod
  • Pods-MyApp: "aggregate" target that simply links with libPods-MyApp-Foo.a
  • Pods-MyApp-Foo: target that builds the lib associated with the Foo pod
  • Pods-MyAppTests: "aggregate" target that simply links with libPods-MyAppTests-Bar.a
  • Pods-MyAppTests-Bar: target that build the lib associated with the Bar pod

And then the MyApp.xcodeproj would have:

  • its "MyApp" target link against libPods.a AND libPods-MyApp.a
  • its "MyAppTests" target link against libPods.a AND `libPods-MyAppTests.a``
    .

Actual Result

In addition to the previously listed ones, the following targets are also generated in Pods.xcodeproj:

  • Pods-MyApp-Common
  • Pods-MyAppTests-Common

_Both compile the files of Commonpod and are duplicates of what the Pod-Common target do_

The Pods-MyApp "aggregate" lib target then links against libPods-MyApp-Common.a in addition to libPods-MyApp-Foo.a, as well as the Pods-MyAppTests "aggregate" lib target links against libPods-MyAppTest-Common.a in addition to libPods-MyAppTests-Bar.a

This leads to duplicate symbols, as the files in the Common pod/library are linked twice, once with libPods-Common.a and once with libPods-MyApp[Tests]-Common.a whereas they should appear in only one aggregate.


The solution is:

  • Keep libsPods.a and libPods-Common.a — and link both "MyApp" and "MyAppTests" against it in addition to linking them against their libPods-MyApp[Tests].a specific aggregate libs — but stop generating libPods-MyApp[Tests]-Common.a
  • Or get rid of the libPods.a and libPods-Common.a targets/libraries and stop linking MyApp and MyAppTests against it — but keep libPods-MyApp[Tests]-Common.a

The first solution seems much more logical to me as it would reflect the configuration described in the Podfile, whereas the last solution better corresponds to the case where the user would have repeated pod "Common" in each target XXX do block)

confirmed

Most helpful comment

Thanks for writing this up, I've hit this on multiple projects but haven't been able to illustrate the problem well enough to post an issue about it.

All 27 comments

Images are worth a thousand words, so here are some pics :)

Actual Result

Duplicate Symbols because libPods.a (that links against libPods-Common.a) and libPods-MyApp.a (that links against libPods-MyApp-Common.a) both contains symbols of the "Common" pod.

actualresult

_Generally only the first App target is linked against libPods.a (that's why there is no arrow from MyAppTests to libPods.a) so the Duplicate Symbols only happen for this first target._

Solution 1

Keep libPods.a for common pods, but then do not generate a target-specific lib for this common pod (and do not link target-specific aggregate libs against them)
Solution 1

_I think that this solution corresponds more to the structure of the Podfile_

Solution 2

Get rid of libPods.a and stop linking against it. Duplicate libPods-<AppTargetName>-Common.a targets for each <AppTargetName>.
Solution 2

_But I think that this solution is more suitable for cases where the user explicitly repeated pod "Common" in each of his/her target XXX do block than for the case of the Podfile presented here._

Alternate solutions

Of course there is also Solution 3 or Solution 4 too…

I also ran into this issue. In fact even after reverting my pod file and running install it was still messed up after having this issue.

Yeah I just run into this too. Test target has all the dependencies that actual target has, and this creates an error.

Thanks for writing this up, I've hit this on multiple projects but haven't been able to illustrate the problem well enough to post an issue about it.

Are there any known workarounds?

I've fixed it with a two-part technique:

  1. Mark my project's primary and Test targets as :exclusive => true, and include every pod they need inside these blocks.
  2. After running pod update, manually fixing the libPods linkage for the main project target from libPods.a to libPods-<project>.a. I can assemble a test project with a couple of commits that demonstrates this, give me a few.

@priteshshah1983 Take a look at these commits: https://github.com/cbowns/RACTest/commit/e3bfc4cd4951d85e8fbafa8d9806e2ae21c474d5 through https://github.com/cbowns/RACTest/commit/39119898b80000579601cba9826327b797eb06d2 (right now, the four commits authored on Feb 27th at https://github.com/cbowns/RACTest/commits/broken-cocoapods)

Thanks a lot @cbowns! That was very helpful.

Yeah this is a major pain point for me as well. My fix is to set the targets as :exclusive => true and remove the common libs from the libPods-<project>.a dependency list manually, leaving the target to link against libPods.a and libPods-<project>.a. Slightly different from @cbowns's fix but probably more in line with what CP should be doing for us.

Some thoughs @irrationalfab or @alloy ? :up:

_I suppose you have a lot on your plate and would understand if you won't have time to fix this soon, but at least any idea about a milestone / target version for the fix, maybe a though about which of the 4 solutions would be the best fit to fix this, and how we could help you implementing the fix?_

Thx guys! :beers:

Issue has been confirmed by @neonichu

I've got some free cycles and would love to contribute to fixing this. (I was a build engineer at Apple on OS X and iOS software for two years, so I've got a disturbing amount of knowledge about the ins and outs of these systems.) What's a good place to start?

Sorry for not replying to this issue before. The problem is related to lack of proper inheritance on CocoaPods and the implicit target definition. Lets start with this last one first:

The implicit target definition is a feature of CocoaPods which was designed (form my understanding) to allow beginners to get up and ready as soon as possible. This is the reason why the following is a valid Podfile:

pod 'ARAnalytics'

What CocoaPods does under the hood is inspecting the user project (inferred to be the only one in the directory of the Podfile, otherwise an error is presented) and links against the first target (inferred to be what you were after if you did't specify a more complex Podfile). Consequently, using the provided example this is more or less equivalent to:

target 'MyApp' do
  pod 'ARAnalytics'
end

So what is happening in the provided is that CocoaPods is linking two libraries (aggregated targets) while by design it should only link to one and this is the cause of the duplicate symbols. Linking only one target is an important design principle as it keeps the integration simple in the user project (any selection of the libraries and aggregation is done in the Pods project).

Finally, as de-integration is not implemented in CocoaPods (luckily there is not much demand for it) if you switch among the two Podfiles you could end up in an inconsistent state.

Regarding the lack of proper inheritance, simply in CocoaPods is not possible to do something like the following:

target 'MyApp' do
  inherit 'Common'
end

target 'MyApp' do
  inherit 'Common'
end

abstract_target 'Common' do
  pod 'ARAnalytics'
end

Moreover, a target always inherits from its parent, so if you create a target that links against the first target of your app and add a dependency to the implicit target definition it will be duplicated as your app will be linked with the two libraries which will include the dependency.

This is one of the biggest issues of CocoaPods, it hasn't been fixed before because it requires a lot of effort:

  • Redesign a new DSL (a good one so we don't need to change it anymore)
  • Make it backwards compatible to the possible extent
  • Re-architect CocoaPods to use it.

However I recently stated working on this (this refactor also blocking the linking per build configuration): https://github.com/CocoaPods/Core/pull/81. I remand also to that issue for the linked discussion (in particular https://github.com/CocoaPods/CocoaPods/issues/840).

@cbowns and those willing to help... This is a complex issue and there is plenty of work to do, the easiest way to get involved with it would be to help defining the DSL. If you instead want to delve in the code immediately I will happily give to you any directions... just let me know what interests you the most.

In the meanwhile I discovered that the unstoppable @kylef uses a solution which works almost as well as a sanctioned DSL for the inheritance: https://github.com/kylef/KFData/blob/master/Tests/Podfile#L5-9

I still think that a DSL solution should be provided but this should be a great workaround in the meanwhile for what I understand to be the usage case of this thread.

Thanks for the feedback @irrationalfab !

Very clever solution by @kylef ! I wonder why I didn't think about using some Ruby to do that myself, after all the DSL is so much integrated that we sometimes forget that a Podfile is initially simply some ruby…

I'll comment about my suggestions in the other threads (in particular #840).

I'm still unclear here, is there a formal/correct way to do this: specify a set of pods common to all targets, and then specify additional pods specific to one of those targets?

Basically get rid of the common target and instead just use a ruby function to include pods in each actual target. Example below copied from https://github.com/CocoaPods/CocoaPods/issues/1729#issuecomment-39510160.

def import_pods
    pod 'KFData', :path => '../'
    pod 'KFData/Compatibility', :path => '../'
    pod 'Expecta'
end

target :ios do
    platform :ios, '5.0'
    link_with 'iOS Tests'
    import_pods
end

target :osx do
    platform :osx, '10.7'
    link_with 'OS X Tests'
    import_pods
    pod 'SomeDepForOSXOnly'
end

What am I doing wrong here? In your copy/pasted example, that's targeting two different platforms, as well as two different targets. I'm trying to setup for different targets on the same platform. With the following podfile, I get duplicate symbol errors:

ld: 211 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Here's my podfile:

def import_pods
  pod 'GoogleAnalytics-iOS-SDK', '~> 3.0.3'
  pod 'AFNetworking', '~> 2.3.1'
end

target :ios do
  platform:ios, '7.0'
  link_with 'myApp'
  import_pods
end

target :ios do
  platform:ios, '7.0'
  import_pods
  link_with 'myAppTests'
  pod 'OCMock','~> 2.2.4'
end

@obuseme you are declaring the same target :ios twice.
@mmerickel example is not really the best template to use as a starting point: instead, don't use link_with at all and use your Xcode's target names in the Podfile's target directive instead.

platform :ios, '7.0'

def common_pods
  pod 'GoogleAnalytics-iOS-SDK', '~> 3.0'
  pod 'AFNetworking', '~> 2.3'
end

target :myApp do
  common_pods
end

target :myAppTests do
  common_pods
  pod 'OCMock','~> 2.2'
end

Hello, AliSoftware. Your example doesnt work in my case (i used last comment as podfile) - i have library as the main target

platform :ios, '7.0'

def common_pods
  pod 'AFNetworking', '~> 2.3'
end

target :MyLib do
  common_pods
end

target :MyLibTests do
  common_pods
end

and tests of course linked with libMyLib.a in link binary with libraries so i have "duplicate symbols for architecture i386". Any suggestions? (afnetworking now is visible from tests but duplicate symbols)

basically its this issue #1688 and i can see some workaround on SO but dont understand what should i put as actual filename

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

@alexburkhay When you use my suggested solution, i.e. not declaring any pod in the root scope of the Podfile but only in target :xxx do blocks, then libPods.a won't be generated anymore, as there won't be any pod declared in the root scope and common to all targets, but only libPods-<TargetName>.a aggregate libraries.

In that case, check that your targets in Xcode are linked against the correct libPods-<TargetName>.a libraries only, and not with libPods.a anymore as this one does not exist anymore in such configuration (but may erroneously remain present from previous pod install that you may have executed before on your project).

Do also clean your project before trying to recompile it, to ensure there is no previously-build libPods.a or any other library that would still exist from a previous build and weren't properly cleared.

@AliSoftware Yes, all correct .. there's no libPods.a and only libPods-<"TargetName">.a in correct places.. i checked it. Duplicates come from problem that i have static library as main target and its included in tests target "link binary with libraries" as libMyLibrary.a (its not a pod lib)
Basically its known and opened issue https://github.com/CocoaPods/CocoaPods/issues/1688
Also i found this blog http://blog.sigmapoint.pl/developing-static-library-for-ios-with-cocoapods/ where u can see that correct way to resolve it is to make empty
target :MyLibTests do
#empty
end

and manually configure your xcode project configuration for tests https://raw.github.com/burczyk/ReposBrowser/master/assets/pods_configuration_tests.png

@alexburkhay In our project we found one another workaround. If your primary target is a library, which is then linked against by some App, then the solution is to ... make the library a pod itself ;) And link the App to the pod (preferably in a form of development pod).

Example:

Primary target: MyLibrary
Another target: MyApp (uses MyLibrary)

Podfile (implicit target is assumed to be MyLibrary)

pod 'X'
pod 'Y'

target :MyApp, :exclusive => true do
    pod 'MyLibrary', :path => '.' #or wherever podspec for your MyLibrary is
    pod 'Z'
end

NOTE: Please remember to not add libMyLibrary.a to the list of linked binaries of MyApp manually. Pods will do it for you.

@AliSoftware Hmmm, clever solution. What's weird is, I get an error where my main target tries to link -lPods anyhow. I've dug through my .xcconfigs and project linker flags and simply can't find where that flag comes from. Did you experience this, or no?

edit: jk jk jk found it! I had a dangling 'lPods' link in the "Link Binary with Libraries" step. I can't even count how many times I've had to make that change. (via https://github.com/CocoaPods/CocoaPods/issues/155#issuecomment-74461617)

This should be fixed by #3550, which is in 0.38.

Was this page helpful?
0 / 5 - 0 ratings