Fat libraries in XCode 5 - iphone

I've been trying to build a static library and then create a binding project from it in Xamarin. Everything was working fine until iOS 7 hit. I had to grab the latest version of the native library and try and build it in XCode 5, but it's been giving me all kinds of problems. I think it might be related to the build process or possibly some changed setting in XCode 5 (vs. 4) but I'm not sure.
I was using this script to build a universal binary which is based of work in this question:
Build fat static library (device + simulator) using Xcode and SDK 4+
One thing I did notice is that previous, in the old iOS 6.1 version of my binary (built in XCode 4), my binary was about 24 Mb, now with XCode 5 it's ballooned to almost 50 Mb! Which is leading me to think that there is something wrong with the compiling and linking step.
Any ideas? Has anybody else encountered problems with universal binaries in XCode 5 (vs 4)?

I'm using the makefile below for my library and it works flawless even with XCode 5 and the iOS7 SDK.
XBUILD=/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild
PROJECT_ROOT=.
PROJECT=$(PROJECT_ROOT)/GIFLibFrontEnd.xcodeproj
TARGET=GIFLibFrontEnd
all: libUniversal.a
libi386.a:
$(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphonesimulator -configuration Release clean build
-mv $(PROJECT_ROOT)/build/Release-iphonesimulator/lib$(TARGET).a $#
libArmv7.a:
$(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphoneos -arch armv7 -configuration Release clean build
-mv $(PROJECT_ROOT)/build/Release-iphoneos/lib$(TARGET).a $#
libArmv7s.a:
$(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphoneos -arch armv7s -configuration Release clean build
-mv $(PROJECT_ROOT)/build/Release-iphoneos/lib$(TARGET).a $#
libArm64.a:
$(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphoneos -arch arm64 -configuration Release clean build
-mv $(PROJECT_ROOT)/build/Release-iphoneos/lib$(TARGET).a $#
libUniversal.a: libi386.a libArmv7.a libArmv7s.a libArm64.a
lipo -create -output lib$(TARGET)Universal.a $^
clean:
-rm -f *.a *.dll
-rm -rf build

Here's a link to a Makefile with the tabs, and I made a little change to separate out the target name from the library name. Thanks very much for this! This solved my problem!

Related

Error While creating Custom Framework and xcframework

I am creating a Framework where i am adding multiple Third Party Libraries manually. and creating a Fat framework SomeFrameworkName.framework using the script below
#!/bin/sh
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
# Step 1. Build Device and Simulator versions
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
# Step 2. Copy the framework structure (from iphoneos build) to the universal folder
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"
# Step 3. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory
SIMULATOR_SWIFT_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/."
if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then
cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"
fi
# Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"
# Step 5. Convenience step to copy the framework to the project's directory
cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"
# Step 6. Convenience step to open the project's directory in Finder
open "${PROJECT_DIR}"
After Archiving i am able to get the framework file but when i try to add that framework into the demo project it gives me multiple errors as follows:
dyld: Library not loaded: #rpath/TestIOS.framework/TestIOS
Referenced from: /private/var/containers/Bundle/Application/E76B5EA8-2E7C-44A4-BDCC-A8AB0B37E500/TestAshishDemo.app/TestDemo
Reason: image not found
dyld: launch, loading dependent libraries
DYLD_LIBRARY_PATH=/usr/lib/system/introspection
DYLD_INSERT_LIBRARIES=/Developer/usr/lib/libBacktraceRecording.dylib:/Developer/usr/lib/libMainThreadChecker.dylib:/Developer/Library/PrivateFrameworks/DTDDISupport.framework/libViewDebuggerSupport.dylib
or else any library names comes in the picture.
and whenever i try to create the xcframework file i get the following error
Failed to build module from its module interface; it may have been damaged or it may have triggered a bug in the Swift compiler when it was produced
i am on a crucial stage to solve this error. Anyone if facing the same issue or faced the same along the way they created framework Please Help me out
XCode Version - 12.5.1
In your target project, did you try to embed the Framework ?
(Old XCode screenshot of mine here)

xcframework does not contain internal frameworks

I am trying to create an .xcframework file for a few third party libraries I'm using. Following the instructions from https://appspector.com/blog/xcframeworks, I
Create platform-specific framework files:
# Archive for device
xcodebuild archive -scheme TestFramework -destination="iOS" -archivePath /tmp/xcf/ios.xcarchive -derivedDataPath /tmp/iphoneos -sdk iphoneos SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES
# Archive for simulator
xcodebuild archive -scheme TestFramework -destination="iOS Simulator" -archivePath /tmp/xcf/iossimulator.xcarchive -derivedDataPath /tmp/iphoneos -sdk iphonesimulator SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES
At this point, I've gotten two frameworks with .swiftmodule files; everything looks good. The frameworks do not come with .swiftinterface files, but I think that is alright as these are objc projects. Then, I
Combine the individual frameworks to form a single xcframework file:
# Build xcframework with two archives
xcodebuild -create-xcframework -framework /tmp/xcf/ios.xcarchive/Products/Library/Frameworks/TestFramework.framework -framework /tmp/xcf/iossimulator.xcarchive/Products/Library/Frameworks/TestFramework.framework -output /tmp/xcf/TestFramework.xcframework
Here, xcodebuild doesn't fail, but it emits a curious error:
No 'swiftinterface' files found within '/Users/user/git/ReactiveObjC/output/xcf/ios.xcarchive/Products/Products/Library/TestFramework.framework/Modules/TestFramework.swiftmodule'.
Crucially, the .xcframework package does not contain the individual frameworks or folders specified in the Info.plist file. Obviously, when I try to link to the xcframework in this state, I get a bunch of "missing symbols" linker errors.
Why is this? What must I do to get xcodebuild to create the xcframework completely?
NB: Manually creating the folders seems to work for now, but this feels wrong and is brittle – Apple can change the way xcodebuild works anytime.
Turns out that adding BUILD_LIBRARY_FOR_DISTRIBUTION = YES to the end of the xcodebuild command was not enough / not working to produce a framework with the required swiftinterface files. I had to go in the settings for the actual project and manually set BUILD_LIBRARY_FOR_DISTRIBUTION to YES, like this answer suggests. After I got the frameworks with the swiftinterface files, I was then able to create the xcframework file using xcodebuild without any problems.
Create XCFramework
XCFramework replaces Fat/Universal framework. It is a bundle which contains multiple .framewok for each architecture/platform. We don't need to use lipo[About] for unite and separate frameworks manually
XCFramework supports libraries with headers and Objective-C
Enable Build Libraries for Distribution(BUILD_LIBRARY_FOR_DISTRIBUTION) for your framework. XCFramework works only with enabled [BUILD_LIBRARY_FOR_DISTRIBUTION]. If not when you run step 3 xcodebuild -create-xcframework you get (or don't)
No 'swiftinterface' files found within '<some_path>/<some_name>.xcarchive/Products/Library/Frameworks/<some_name>.framework/Modules/<some_name>.swiftmodule'
Build/archive .framework for different architectures/platforms
xcodebuild archive \
-workspace ModuleName.xcworkspace \
-scheme "schemaName" \
-sdk "iphoneos" \
-arch arm64 \
-archivePath "<some_path>/ArchiveName1.xcarchive" \
SKIP_INSTALL=NO \
xcodebuild archive \
-workspace ModuleName.xcworkspace \
-scheme "schemaName" \
-sdk "iphonesimulator" \
-arch x86_64 \
-archivePath "<some_path>/ArchiveName2.xcarchive" \
SKIP_INSTALL=NO \
SKIP_INSTALL[About] one of key point to include framework inside archive
Create XCFramework using xcodebuild -create-xcframework
xcodebuild -create-xcframework \
-framework "<some_path>/ArchiveName1.xcarchive/Products/Library/Frameworks/ModuleName.framework" \
-debug-symbols "<some_path>/ArchiveName1.xcarchive/dSYMs/${schemes[$n]}.ModuleName.dSYM" \
-framework "<some_path>/ArchiveName2.xcarchive/Products/Library/Frameworks/ModuleName.framework"
-debug-symbols allows to add .dSYM[About] and .BCSymbolMap[About]
Converting to XCFramework from Cocoapods
For my scenario, I'm trying to build XCFrameworks from the FAT/lipo frameworks generated by cocoapods.
I added this to the end of my podfile:
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
end
end
end
This ensured the frameworks were compiled correctly and I was able to run the normal xcodebuild -create-xcframework ... command to generate the XCFramework.
Note: I'm also using the cocoapods-binary plugin to pre-build all the pods frameworks.
I faced the same issue. It turned out that Xcode 13's build settings override the SKIP_INSTALL=NO flag in the command line. Once I set this field in project's build setting to NO (and made sure on the way that the Build Library For Distribution is set to YES) I managed to create the xcframework file successfully.
I have added readme file with full details how to create xc framework.
https://github.com/awasthi027/Xcode_lib_framework_issues/blob/master/README.md
Follow instruction from this for creating xc framework

Swift private Framework with backward xcode version compatibility

This is my first framework using Swift language and it's private framework. It's worked as expected until new Xcode 9.3 release. After upgrading newer Xcode got an issue like this Module compiled with Swift 4.0.3 cannot be imported in Swift 4.1.
After that only I realized framework doesn't support auto version (Xcode/Swift) upgrade, Did some search found there is a Universal or Fat Binary Framework single build instead of separate build for real device and simulator which I missed in previously.
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
# Make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
# Next, work out if we're in SIM or DEVICE
xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
# Step 2. Copy the framework structure (from iphoneos build) to the universal folder
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"
# Step 3. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory
BUILD_PRODUCTS="${SYMROOT}/../../../../Products"
cp -R "${BUILD_PRODUCTS}/Debug-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/." "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"
# Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_PRODUCTS}/Debug-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"
# Step 5. Convenience step to copy the framework to the project's directory
cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"
# Step 6. Convenience step to open the project's directory in Finder
open "${PROJECT_DIR}"
fi
At Edit Scheme -> Archive -> Post-Action -> Run Script
This time I am used Swift Compiler - Language -> Swift 3.3
And I got expected output this time too in Xcode 9.3 with Swift 4.1
When I try to run the project with this framework at Xcode 9.2 it's show error like this
Module compiled with Swift 4.1 cannot be imported in Swift 3.2.3:
Try to resolve with backward compatibility and I didn't find. How can I run this framework in Xcode 9.2
Framework support: Swift and Objective C, Min OS iOS 8.0
**Update**
Also got something like this error
compiled with newer version of Swift language (unknown ABI version 0x06) than previous files (4.0) file 'MAC location' for architecture armv7
And tried this to my project

Swift fat framework w/Objective-C Cocoapod

I've built a framework in Swift. The framework uses Cocoapods, one of the pods is written in Objective C.
I also use a custom script to make the framework a fat framework so it supports 32/64 bit systems. (This runs in a separate target on the project and I'm wondering if that has something to do with it?)
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
if [ "true" == ${ALREADYINVOKED:-false} ]
then
echo "RECURSION: Detected, stopping"
else
export ALREADYINVOKED="true"
// Step 1. Build Device and Simulator versions
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
//Step 2. Copy the framework structure (from iphoneos build) to the universal folder
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"
// Step 3. Copy Swift modules (from iphonesimulator build) to the copied framework directory
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/." "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"
// Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"
// Step 5. Convenience step to copy the framework to the project's directory
cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"
// Step 6. Convenience step to open the project's directory in Finder
open "${PROJECT_DIR}"
fi
When I create this fat framework and put it into a project I'd like to use it on the compiler always fails b/c the project can't see the objective C cocoa pod module.
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ld: framework not found Pusher for architecture x86_64
It is not found for any architecture when I change the build platform.
Any solution where I can produce a framework that I can use in a separate Xcode project (for all iOS or OS X) would be awesome.
You can't run xcodebuild with -target when using CocoaPods. When you use -target, Xcode will only consider the active project and fail to pull in the Pod dependencies, similar to if you just opened the project file in Xcode and tried building.
You should be running xcodebuild -workspace "${PROJECT_NAME}.xcworkspace" -scheme "${PROJECT_NAME}" ..., assuming that CocoaPods generated the workspace and Xcode generated a scheme using the target name. You will also need to make sure your scheme is marked as shared if running this on another device.
Once your framework is built you will need to include it and the frameworks it depends on in apps that will be using it. For your framework, that means including it in General > Embedded Binaries and General > Linked Frameworks and Libraries. For frameworks you depend on, (e.g. AlamoFire), you could instruct users to include it in their Podfile, you could package it and ship it along with your framework, or you could do both and let the user do what works for them.
Apparently you are missing the 64bit architecture for Simulator.
When you build a target from Xcode, depending on what kind of simulator you have selected - the produced library will contains i386 or x86_64 respectively for selected 32bit or 64bit version of the simulator.
I guess that the cli build is producing only i386 version.
You can try to set the architectures in the script:
xcodebuild -target "${PROJECT_NAME}" ARCHS="i386 x86_64" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
As an alternative You can try to build by hand using 64bit simulator (iPhone 5S +), then extract the missing architecture and then put it into the final library using lipo command.

xcodebuild: simulator or device?

How do I specify to xcodebuild (the command line tool) whether I want to build for the simulator or device?
An Xcode build from the command line looks like:
xcodebuild -configuration ${BUILD_TYPE} -target ${TARGET_NAME} -arch ${CPU_ARCHITECTURE} -sdk ${SIMULATOR_OR_IOS_SDK}
BUILD_TYPE is something like "Release" or "Debug" (those are the defaults, you may have added others to the project)
TARGET_NAME is the name of the target you are building (by default the same name as your project)
CPU_ARCHITECTURE is the CPU you are building for, one of:
i386, armv6, armv7
Use i386 for simulator builds, and use either armv6 or armv7 for device builds - note that some other devices cannot run armv7 code, so usually when building libraries it's a good idea to build all of these architectures and then glue them together using lipo.
SIMULATOR_OR_IOS_SDK is what you are looking for, it's either iphoneos or iphonesimulator. Those values use the latest version of the SDK that the installed Xcode supports, you can get a list of supported SDK's with:
xcodebuild -showsdks
Which returns a list like:
Mac OS X SDKs:
Current Mac OS -sdk
Mac OS X 10.6 -sdk macosx10.6
iOS SDKs:
iOS 4.2 -sdk iphoneos4.2
iOS Simulator SDKs:
Simulator - iOS 3.2 -sdk iphonesimulator3.2
Simulator - iOS 4.0 -sdk iphonesimulator4.0
Simulator - iOS 4.1 -sdk iphonesimulator4.1
Simulator - iOS 4.2 -sdk iphonesimulator4.2
xcodebuild has more flags than that, but those are the ones you'd commonly use after using Xcode to set up the build properties. You don't have to use all of them, but it's probably a good idea to be clear about what you are building - otherwise I believe your last settings are used.
i find the -xcconfig flag quite useful. this option allows you to specify a path to an xcconfig (build settings file). within an xcconfig, you may #include other xcconfig files.