Cocoapods: "Multiple commands produce" error when building unit test target without a host

Created on 25 Sep 2019  Âˇ  25Comments  Âˇ  Source: CocoaPods/CocoaPods

Report

What did you do?

  1. Set up a pod that vendors a binary framework (e.g. Library.framework) and a dSYM artifact (e.g. Library.framework.dSYM).

  2. Set up a project that has a unit test target without a host application.

  3. Run pod install.

  4. Built the unit test target for testing.

What did you expect to happen?

Compiling succeeds and dependency is available in both main target and unit test target.

What happened instead?

Compiling fails because:

Multiple commands produce '/Users/Adrian/Library/Developer/Xcode/DerivedData/Demo/Build/Products/Debug-iphonesimulator/Library.framework.dSYM':
1) That command depends on command in Target 'Demo' (project 'Demo'): script phase “[CP] Embed Pods Frameworks”
2) That command depends on command in Target 'DemoTests' (project 'Demo'): script phase “[CP] Embed Pods Frameworks”

Workarounds

  1. Setting a test host for the unit test target makes it work.

  2. Switching to the legacy build system makes it work.

  3. Adding disable_input_output_paths: true option makes it work.

CocoaPods Environment

Stack

   CocoaPods : 1.8.0
        Ruby : ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin18]
    RubyGems : 3.0.3
        Host : Mac OS X 10.14.6 (18G95)
       Xcode : 11.0 (11M392r)
         Git : git version 2.23.0
Ruby lib dir : /Users/Adrian/.rbenv/versions/2.6.3/lib
Repositories : master - git - https://github.com/CocoaPods/Specs.git @ aff37984f83a304d8f1f3c5bd909f8f377617acc

Installation Source

Executable Path: /Users/Adrian/.rbenv/versions/2.6.3/bin/pod

Plugins

cocoapods-deintegrate : 1.0.4
cocoapods-keys        : 2.1.0
cocoapods-plugins     : 1.0.0
cocoapods-search      : 1.0.0
cocoapods-stats       : 1.1.0
cocoapods-trunk       : 1.4.0
cocoapods-try         : 1.1.0

Podfile

source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!

def shared_pods
   pod "Kingfisher", podspec: "./Kingfisher/Kingfisher.podspec"
end

target 'Demo' do
    shared_pods
end

target 'DemoTests' do
    shared_pods
end

Project that demonstrates the issue

Attached: 15589-9.0.0-ios.zip. The archive consists of:

  • Demo.xcodeproj that has the set up described above.
  • Kingfisher.framework and .dSYM used by Podfile.
hard help wanted confirmed workaround available defect

Most helpful comment

We investigated the issue further, this is actually a much more complex change so it will have to be in 1.10.0 release.

The issue here is that CocoaPods treats dSYMs as if they were frameworks during the embedding stage versus all that is needed is to just copy the vendored dSYM into the Products dir.

I am re-implemeting this from scratch and will have a PR soon.

All 25 comments

Hm maybe a bug but thanks for the report and sample app! If its identified to be an issue we can fix it for 1.8.1 release.

It's because your DemoTests depends on DemoApp but it does not use it as its app host as you said initially.

You can also remove the DemoApp dependency from the 'Dependencies' phase in your DemoTests target.

I understand that the workarounds... work. 😄

But target dependency only describes which targets are built _before_ the current one. Having a unit test target without a test host is a valid setup that should be supported while using xcfilelists.

This setup _is_ supported for open source pods that don't have the need to define dSYMs in preserve_paths. It only breaks if there's a dSYM. That's why I believe it's an issue in file lists generated by CocoaPods.

Its probably a bug yes in CocoaPods. we need to detect if the dependency is set but _not_ the app host.

It seems Xcode does not complain in the case in which the app host is set...this will require a change in CocoaPods to detect that case.

This started happening for me on the xcode 11.

In build phases, we have custom fonts included in Copy Bundle Resources and we also had them as input and output files in Copy Pods Resources. In order for the build to work, we have to remove them from Copy Pods Resources, but Pod install/update re-adds them here.

This started happening for me on the xcode 11.

In build phases, we have custom fonts included in Copy Bundle Resources and we also had them as input and output files in Copy Pods Resources. In order for the build to work, we have to remove them from Copy Pods Resources, but Pod install/update re-adds them here.

We have same problem.

I do not think the last two comments are the same issue. Rather a different one and possibly a misconfiguration of the project.

For the original issue this is definitely confirmed. We use ${DWARF_DSYM_FOLDER_PATH}/Kingfisher.framework.dSYM for the output path of each dSYM because that is where Xcode copies dSYMs too. It seems that this is triggered as long as the test target does not use the target app as its app host.

I've hit a similar issue in a bit different setup. So I have an app target (App) that has the following dependencies:

  • B, a developmeent pod with a vendored framework and dSYM.
  • A, a development pod that also depends on B and has a hostless test spec.

Here's the error message:

Multiple commands produce '/path/to/Build/Products/Debug-iphonesimulator/B.framework.dSYM':
1) That command depends on command in Target 'A-Unit-Tests' (project 'Pods'): script phase “[CP] Embed Pods Frameworks”
2) That command depends on command in Target 'App' (project 'App'): script phase “[CP] Embed Pods Frameworks”

We have a very difficult non trivial project structure and use cocoapods-binary plugin.

In case of deactivation cocoapods-binary this issue are not reproduced for our project.

But cocoapods-binary are very useful for us and I solve issue by running script right after pod install

#!/bin/bash -l
set -e
set -o pipefail

full_local_path=$(dirname "$0")

for pfx in "Pods-UIKitDemo" "Pods-Settings" "*TestApp" "*Tests"; do
    find $full_local_path/../Pods -type f -name "$pfx-frameworks-Debug-output-files.xcfilelist" -exec sed -E -i '' '/.*framework\.dSYM$/d' {} \;
done

This script are not universal.
If you want to use it you should change string for pfx in "Pods-UIKitDemo" "Pods-Settings" "*TestApp" "*Tests"; do

For me (Xcode 11.3.1, CP 1.8.4) switching to "Legacy Build System" in workspace settings fixes the build issue but requires me to switch to the system each time I want to run tests. This is really troublesome.

We investigated the issue further, this is actually a much more complex change so it will have to be in 1.10.0 release.

The issue here is that CocoaPods treats dSYMs as if they were frameworks during the embedding stage versus all that is needed is to just copy the vendored dSYM into the Products dir.

I am re-implemeting this from scratch and will have a PR soon.

@akashivskyy can you try #9547 ?

@dnkoutso I confirm that the sample project I attached now builds with #9547. Thank you! ❤️

awesome! Thanks @akashivskyy! I pushed another update to the branch that fixed a critical issue with xcfilelists and output paths. This should land today (PST timezone)

One more follow up to use DWARF_DSYM_FOLDER_PATH instead https://github.com/CocoaPods/CocoaPods/pull/9552. This ensures again dSYMs are copied correctly for archiving.

@akashivskyy can you try #9547 ?

sorry just want to asking, how i implement this one?

@dnkoutso Now that 1.10 is released has this been fixed?

Looks like closed and marked for this release so it should be yes.

Seeing this issue with similar setup to @navartis, using binary in prebuild because Firebase and other google pods are massive (15-20 min) compile times
With cocoapods-binary I get

Multiple commands produce '/Users/felip/Library/Developer/Xcode/DerivedData/CashBack-cuszxqnldfepigfapfsmaoloyjan/Build/Products/Debug-iphoneos/GTMSessionFetcher/GTMSessionFetcher.framework.dSYM':
1) That command depends on command in Target 'GTMSessionFetcher' (project 'Pods'): script phase “[CP] Copy dSYMs”
2) That command depends on command in Target 'GTMSessionFetcher' (project 'Pods'): script phase “[CP] Copy dSYMs”
3) That command depends on command in Target 'GTMSessionFetcher' (project 'Pods'): script phase “[CP] Copy dSYMs”

Multiple commands produce '/Users/felip/Library/Developer/Xcode/DerivedData/CashBack-cuszxqnldfepigfapfsmaoloyjan/Build/Products/Debug-iphoneos/nanopb/nanopb.framework.dSYM':
1) That command depends on command in Target 'nanopb' (project 'Pods'): script phase “[CP] Copy dSYMs”
2) That command depends on command in Target 'nanopb' (project 'Pods'): script phase “[CP] Copy dSYMs”
3) That command depends on command in Target 'nanopb' (project 'Pods'): script phase “[CP] Copy dSYMs”

Multiple commands produce '/Users/felip/Library/Developer/Xcode/DerivedData/CashBack-cuszxqnldfepigfapfsmaoloyjan/Build/Products/Debug-iphoneos/BoringSSL-GRPC/openssl_grpc.framework.dSYM':
1) That command depends on command in Target 'BoringSSL-GRPC' (project 'Pods'): script phase “[CP] Copy dSYMs”
2) That command depends on command in Target 'BoringSSL-GRPC' (project 'Pods'): script phase “[CP] Copy dSYMs”
3) That command depends on command in Target 'BoringSSL-GRPC' (project 'Pods'): script phase “[CP] Copy dSYMs”

Multiple commands produce '/Users/felip/Library/Developer/Xcode/DerivedData/CashBack-cuszxqnldfepigfapfsmaoloyjan/Build/Products/Debug-iphoneos/abseil/absl.framework.dSYM':
1) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
2) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
3) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
4) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
5) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
6) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
7) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
8) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
9) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
10) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
11) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
12) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
13) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
14) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
15) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
16) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
17) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
18) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
19) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
20) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
21) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
22) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
23) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
24) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
25) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
26) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
27) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
28) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
29) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
30) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
31) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
32) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
33) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
34) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
35) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
36) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
37) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
38) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
39) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
40) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
41) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
42) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
43) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
44) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
45) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
46) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
47) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
48) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
49) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”
50) That command depends on command in Target 'abseil' (project 'Pods'): script phase “[CP] Copy dSYMs”

Multiple commands produce '/Users/felip/Library/Developer/Xcode/DerivedData/CashBack-cuszxqnldfepigfapfsmaoloyjan/Build/Products/Debug-iphoneos/gRPC-Core/grpc.framework.dSYM':
1) That command depends on command in Target 'gRPC-Core' (project 'Pods'): script phase “[CP] Copy dSYMs”
2) That command depends on command in Target 'gRPC-Core' (project 'Pods'): script phase “[CP] Copy dSYMs”
3) That command depends on command in Target 'gRPC-Core' (project 'Pods'): script phase “[CP] Copy dSYMs”

Multiple commands produce '/Users/felip/Library/Developer/Xcode/DerivedData/CashBack-cuszxqnldfepigfapfsmaoloyjan/Build/Products/Debug-iphoneos/gRPC-C++/grpcpp.framework.dSYM':
1) That command depends on command in Target 'gRPC-C++' (project 'Pods'): script phase “[CP] Copy dSYMs”
2) That command depends on command in Target 'gRPC-C++' (project 'Pods'): script phase “[CP] Copy dSYMs”
3) That command depends on command in Target 'gRPC-C++' (project 'Pods'): script phase “[CP] Copy dSYMs”

Multiple commands produce '/Users/felip/Library/Developer/Xcode/DerivedData/CashBack-cuszxqnldfepigfapfsmaoloyjan/Build/Products/Debug-iphoneos/GoogleUtilities/GoogleUtilities.framework.dSYM':
1) That command depends on command in Target 'GoogleUtilities' (project 'Pods'): script phase “[CP] Copy dSYMs”
2) That command depends on command in Target 'GoogleUtilities' (project 'Pods'): script phase “[CP] Copy dSYMs”
3) That command depends on command in Target 'GoogleUtilities' (project 'Pods'): script phase “[CP] Copy dSYMs”
4) That command depends on command in Target 'GoogleUtilities' (project 'Pods'): script phase “[CP] Copy dSYMs”
5) That command depends on command in Target 'GoogleUtilities' (project 'Pods'): script phase “[CP] Copy dSYMs”
6) That command depends on command in Target 'GoogleUtilities' (project 'Pods'): script phase “[CP] Copy dSYMs”
7) That command depends on command in Target 'GoogleUtilities' (project 'Pods'): script phase “[CP] Copy dSYMs”
8) That command depends on command in Target 'GoogleUtilities' (project 'Pods'): script phase “[CP] Copy dSYMs”

Without cocoa-binary it doesn't get the errors in xcode

@FaultyJuggler Did you figure this out? I'm seeing the same.

same with binary ..

Yes, I am seeing the same as @FaultyJuggler in the latest version of cocoapods 1.10.0

EDIT: find a workaround here: https://github.com/CocoaPods/CocoaPods/issues/10373

Same happening with :binary => true
Tried workaround #10373 with no luck.

Found this post integrate script that did the trick, if someone is looking for:

post_integrate do |installer|
  installer.pods_project.targets.each do |target|
    target.shell_script_build_phases.each do |phase|
      script = phase.shell_script
      if script.include? "-copy-dsyms.sh\""
        script = script.delete_prefix "\"${PODS_ROOT}/"
        script = script.delete_suffix "\"\n"
        script = "Pods/" + script

        contents = File.read(script)
        contents = contents.gsub(/-av/, "-r -L -p -t -g -o -D -v")
        File.open(script, "w") do |file|
          file.puts contents
        end
      end
    end
  end
end
Was this page helpful?
0 / 5 - 0 ratings

Related issues

iosdev-republicofapps picture iosdev-republicofapps  Âˇ  3Comments

sonu5 picture sonu5  Âˇ  3Comments

k06a picture k06a  Âˇ  3Comments

dawnnnnn picture dawnnnnn  Âˇ  3Comments

Mingmingmew picture Mingmingmew  Âˇ  3Comments