Cocoapods: strip_invalid_archs() breaks vendored frameworks

Created on 5 Apr 2018  路  14Comments  路  Source: CocoaPods/CocoaPods

A framework looks like that:

FW.framework
FW.framework/FW -> Versions/Current/FW
FW.framework/Headers -> Versions/Current/Headers
FW.framework/Resources -> Versions/Current/Resources
FW.framework/Versions
FW.framework/Versions/A
FW.framework/Versions/Current -> A
FW.framework/Versions/A/FW
FW.framework/Versions/A/Headers
FW.framework/Versions/A/Resources

Note that the 2nd line above is a symlink to the real binary which resists in the A folder. However, if that binary is a fat binary and you call strip_invalid_archs() to thin it, the resulting framework looks like that:

FW.framework
FW.framework/FW
FW.framework/Headers -> Versions/Current/Headers
FW.framework/Resources -> Versions/Current/Resources
FW.framework/Versions
FW.framework/Versions/A
FW.framework/Versions/Current -> A
FW.framework/Versions/A/FW
FW.framework/Versions/A/Headers
FW.framework/Versions/A/Resources

Note that the 2nd line is no symlink anymore. Now the framework contains two binaries. A thin one FW.framework/FW and the original, unmodified fat one FW.framework/Versions/A/FW.

This results in codesign to fail with the following error:

bundle format is ambiguous (could be app or framework)

All 14 comments

I assume this happens pre-1.5.0 too correct?

@CodingMarkus can you also upload a quick sample app?

It happens because of this code line

lipo -remove "$arch" -output "$binary" "$binary" || exit 1

If you need a test universal binary, just download a freeware, like Bonjour Browser:
http://www.tildesoft.com/#RendezvousBrowser

And here is the problem:

# lipo -info "Bonjour Browser.app/Contents/MacOS/Bonjour Browser" 
Architectures in the fat file: Bonjour Browser.app/Contents/MacOS/Bonjour Browser are: ppc i386

# ln -s "Bonjour Browser.app/Contents/MacOS/Bonjour Browser" test

# ls -l test
...  test -> Bonjour Browser.app/Contents/MacOS/Bonjour Browser

# lipo -info test
Architectures in the fat file: test are: ppc i386 

# arch=ppc binary=test ; ( lipo -remove "$arch" -output "$binary" "$binary" )

# lipo -info test
Architectures in the fat file: test are: i386

# ls -l test
... test

# lipo -info "Bonjour Browser.app/Contents/MacOS/Bonjour Browser"
Architectures in the fat file: Bonjour Browser.app/Contents/MacOS/Bonjour Browser are: ppc i386 

lipo does not follow symlinks when writing output files, it's that easy, so the symlink is replaced by an actual file. If you want to preserve existing symlinks, you have to do a readlink first and then operate on the real binary, just as install_framework() is doing it.

@CodingMarkus I've already reprod and have a fix. Just testing it out a bit, sorry should have updated the issue.

@CodingMarkus what does file <path/to/your/.framework/executable output for you?

@CodingMarkus this is not about lipo but codesign itself, I have a feeling it has to do with the binary type instead of the symlinks.

This is all about lipo and the way you are calling it. A framework containing a binary at top level is not allowed on macOS, it must be a symlink and it was a symlink prior to lipo destroying it. file says exactly what it supposed to say after the lipo-strip.

I lust replaced

lipo -remove "$arch" -output "$binary" "$binary" || exit 1

with

realBinary="${binary}"
if [ -L "${realBinary}" ]; then
    echo "Symlinked..."
    dirname="$(dirname "${realBinary}")"
    realBinary="${dirname}/$(readlink "${realBinary}")"
fi
lipo -remove "${arch}" -output "${realBinary}" "${realBinary}" || exit 1

and everything works perfectly.

Cool thanks again will try it out and make a PR.

@CodingMarkus I really need you to share a project or the framework you are using. I just tried again locally with a macOS framework and the process works and yes it has symlinks and the structure you mention.

I am sorry this has been annoying but something else is off here. Please do share one so I can reproduce.

I cannot share anything with you. The project in question is commercial, it's huge, has tons of dependencies which I must not share either and the vendored framework is commercially licensed and thus also not shareable. If you need to reproduce it, just use any project and add any pod with vendored universal framework. But now that I have a fix that works for me, I don't care for this issue to fixed any longer as for me it's fixed (we have to run pod update or pod install maybe twice a year and then I can re-fix the script by hand every time). I shared the code that fixes it, so take it or keep shipping a broken Cocoapods, I gave you all the help I could.

Cool story, then it will be fixed when and if somebody gets on it

Hi @dnkoutso I am actually running into the same problem and it should actually be pretty easy for you to reproduce.

My company has a pretty popular pod, we've created a dynamic version of the framework called OneSignalDynamic. If you try to install this pod, you will see the bundle format is ambiguous error when building:

mkdir -p /Users/bradhesse/Library/Developer/Xcode/DerivedData/OneSignalDemo-ezxexrhgiimhyuctbdzduqnabevp/Build/Products/Debug-iphoneos/OneSignalDemo.app/Frameworks
rsync --delete -av --filter P .*.?????? --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "/Users/bradhesse/onesignal/OneSignal-iOS-SDK/Examples/SwiftExample/Pods/OneSignalDynamic/iOS_SDK/OneSignalSDK/Framework/Dynamic/OneSignal.framework" "/Users/bradhesse/Library/Developer/Xcode/DerivedData/OneSignalDemo-ezxexrhgiimhyuctbdzduqnabevp/Build/Products/Debug-iphoneos/OneSignalDemo.app/Frameworks"
building file list ... done
OneSignal.framework/
OneSignal.framework/OneSignal
OneSignal.framework/Resources -> Versions/A/Resources
OneSignal.framework/Versions/
OneSignal.framework/Versions/Current -> A
OneSignal.framework/Versions/A/
OneSignal.framework/Versions/A/OneSignal
OneSignal.framework/Versions/A/Resources/
OneSignal.framework/Versions/A/Resources/Info.plist

sent 10275640 bytes  received 122 bytes  20551524.00 bytes/sec
total size is 10273944  speedup is 1.00
/usr/bin/codesign --force --sign 02C02F762EE1ACEB148CB10C2B5F0E6A43F8F274  --preserve-metadata=identifier,entitlements '/Users/bradhesse/Library/Developer/Xcode/DerivedData/OneSignalDemo-ezxexrhgiimhyuctbdzduqnabevp/Build/Products/Debug-iphoneos/OneSignalDemo.app/Frameworks/OneSignal.framework'
/Users/bradhesse/Library/Developer/Xcode/DerivedData/OneSignalDemo-ezxexrhgiimhyuctbdzduqnabevp/Build/Products/Debug-iphoneos/OneSignalDemo.app/Frameworks/OneSignal.framework: replacing existing signature
/Users/bradhesse/Library/Developer/Xcode/DerivedData/OneSignalDemo-ezxexrhgiimhyuctbdzduqnabevp/Build/Products/Debug-iphoneos/OneSignalDemo.app/Frameworks/OneSignal.framework: bundle format is ambiguous (could be app or framework)
Command PhaseScriptExecution failed with a nonzero exit code

This appears to be caused by the Cocoapods Embed Pods Frameworks script breaking symlinks in my framework. It seems to be related to the shell script that strips unused architectures.

Is there anything I can do to fix this on my end? Even if I test with 1.6.0 beta 2 the problem still happens for me and our users. thanks!

Best and fastest way is to open a new issue and provide the framework as an example with reproducible steps.

@Nightsd01 Did you resolve this issue? I'm facing the exact same issue as you, on OneSignal as well 馃槃 Running cocoapods 1.7.0. Any help?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

5SMNOONMS5 picture 5SMNOONMS5  路  3Comments

steffendsommer picture steffendsommer  路  3Comments

pronebird picture pronebird  路  3Comments

soleares picture soleares  路  3Comments

hmistry picture hmistry  路  3Comments