How to generate Xcode project using SPM with extra build flags? - swift

I am creating a package that depends on OpenSSL on macOS. To use SPM to build the package, I need to pass it some build/linker flags:
swift build -Xlinker -L/usr/local/opt/openssl/lib -Xcc -I/usr/local/opt/openssl/include
The problem is that when I build my xcode project using SPM, I cannot pass the flags. In theory I would think the following would work,
swift package generate-xcodeproj -Xlinker -L/usr/local/opt/openssl/lib -Xcc -I/usr/local/opt/openssl/include
but it doesn't and once Xcode project is generated, to build in it, I need to go into the Settings and update the appropriate paths.
This is obviously ugly and also it wouldn't work for proper CI workflow, which depend on features that are currently only supported by xcode project and not SPM.
I have already tried passing these values when doing a command line xcodebuild like the following, but that also didn't work.
xcodebuild -project MyProj.xcodeproj -scheme MyProjScheme HEADER_SEARCH_PATHS=/usr/local/opt/openssl/include LIBRARY_SEARCH_PATHS=/usr/local/opt/openssl/lib build
Any suggestions?

To create an Xcode project with additional flags, we need to do the following 3 steps:
Step 1 - Obtain the proper mapping between compiler/linker flags and the equivalent xcode settings. In this case,
-Xlinker -L/usr/local/opt/openssl/lib -Xcc -I/usr/local/opt/openssl/include
is equivalent to
HEADER_SEARCH_PATHS=/usr/local/opt/openssl/include
LIBRARY_SEARCH_PATHS=/usr/local/opt/openssl/lib
Step 2 - Create an .xcconfig file. For example,
$ less openssl.xcconfig
HEADER_SEARCH_PATHS = /usr/local/opt/openssl/include
LIBRARY_SEARCH_PATHS = /usr/local/opt/openssl/lib
Step 3 - Use the --xcconfig-overrides flag with generate-xcodeproj to make the project use the config file.
swift package generate-xcodeproj --xcconfig-overrides openssl.xcconfig
Now the Xcode project builds out of the box and xcodebuild can be used without any extra flags.

Related

Is it possible to produce an XCFramework from a Swift Package with dependencies?

I want to be able to take an internal SPM package, which depends on several other internal (and one external) SPM packages, and compile it into an XCFramework, using a series of xcodebuild like you would for a framework project. For example, I have PackageB, which references PackageA, and I'm trying to build PackageB.xcframework. The first step would be:
xcodebuild -scheme PackageB -destination "generic/platform=iOS" -configuration Release ARCHS="arm64" BUILD_DIR="./Build
The tool's output indicates that the dependency packages are being resolved, but they are then not recognized by the compiler (i.e. code in PackageB which references PackageA will not compile because PackageA is unknown at that point).
Thanks for any pointers.
You can create xcframework from swift package by using swift-create-xcframework command line tool, there is also GitHub action available for this.
If you don't want to use this tool, you can generate Xcode project by using swift package generate-xcodeproj command:
OVERVIEW: Generates an Xcode project. This command will be deprecated soon.
USAGE: swift package generate-xcodeproj
OPTIONS:
--xcconfig-overrides
Path to xcconfig file
--output Path where the Xcode project should be generated
--legacy-scheme-generator
Use the legacy scheme generator
--watch Watch for changes to the Package manifest to
regenerate the Xcode project
--skip-extra-files Do not add file references for extra files to the
generated Xcode project
--version Show the version.
-h, -help, --help Show help information.
and then create xcframework with the generated Xcode project.

How can I build a Swift Package for iOS over command line?

In Xcode, I can select my destination as a "generic iOS device" or any iOS simulator, and my package will build platform-specific code for ios.
Via command line "swift build" only builds my target for macOS.
I want to build the target for iOS for CI purposes. The problem with building for macOS is that UIKit-specific code won't be built.
For example:
#if canImport(UIKit)
// some invalid code
#endif
The invalid code will not be noticed and will pass the build phase.
Ideally, I could say something like swift build -platform iOS. Is there a way to do something like this?
At time of writing (Feb 16, 2019), a working solution is:
swift build -v \
-Xswiftc "-sdk" \
-Xswiftc "`xcrun --sdk iphonesimulator --show-sdk-path`" \
-Xswiftc "-target" \
-Xswiftc "x86_64-apple-ios13.0-simulator"
This command uses -Xswiftc to workaround the issue by overriding the sdk from macOS to iphonesimulator.
Strictly we add these flags so developers can work around issues, but they also should report a bug so that we can provide a proper solution for their needs.
Source
So I'm guessing there will be a more elegant solution in the future.
Starting with Xcode 11, xcodebuild supports SwiftPM packages out of the box.
An example invocation would look like this:
xcodebuild -scheme Foo \
-destination 'platform=iOS Simulator,OS=13.5,name=iPhone 11 Pro'
where Foo would be the name of the library product you're trying to build. You can
get the full list of available schemes for you SwiftPM package with xcodebuild -list.
You can get the list of available destinations for a given scheme with this invocation:
xcodebuild -showdestinations -scheme Foo

xcodebuild -create-framework error: unable to read the file

I have watched "binary framework in swift" and tried to build xcframework using xcodebuild -create-framework but it is not working properly.
I enabled "Build libraries for Distribution", then I archived and then used the command
xcodebuild -create-framework -framework /path/sample.xarchive -output sample.xcframework
But it is showing an error "unable to read the file at /path/sample/sample".
I am not sure what I am missing.
Sysytem Info:
MacOS: Catalina beta 1
Xcode 11
Here are step by step instructions, I think you might be missing step 2:
1) Set Build Library for Distribution in the build settings for the target framework to YES
2) Again in the build settings, set Skip Install to NO otherwise the framework won't show up in the Archive output folder.
3) Archive from the Xcode Product menu after selecting your Generic iOS Device the output will appear in the Organizer. Control-Click on the Archive. Select Show in Finder Drag that to the terminal to get the path to the archive and append the path (yellow part is the dragged path, gray is navigated in subfolders). In this case it looks like this, I used the ~ to avoid showing entire path.
~/Library/Developer/Xcode/Archives/2019-06-22/Output\ 6-22-19,\ 11.50\ AM.xcarchive/Products/Library/Frameworks/MyFramework.framework
4) Then create the XCFramework by inserting the command in front of the above path:
xcodebuild -create-xcframework -output Output.xcframework -framework ~/Library/Developer/Xcode/Archives/2019-06-22/Output\ 6-22-19,\ 11.50\ AM.xcarchive/Products/Library/Frameworks/MyFramework.framework
5) You then should see the output:
xcframework successfully written out to: ~/Project/Output.xcframework
I expect that someday soon Xcode will add a the ability to directly create the XCFramework without the command line.
You have to do a two step process via the command line.
xcodebuild archive
This will archive the framework and stick it likely in the build directory of your project.
xcodebuild -create-xcframework -output FrameworkName.xcframework -framework build/Release-iphoneos/ArchivedFramework.framework
This should successfully generate the XCFramework.
You typed the command wrong:
xcodebuild -create-xcframework -framework /path/sample.xarchive -output sample.xcframework
In my case it was failing for iPhone as arm64 architecture was added in the Excluded Architecture in Build Settings

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.

Xcode 4 - clang error

I'm migrating from Xcode 3.5 to Xcode 4 and
while I'm trying to Archiving my app for AdHoc distribution.
I received this error
Precompile MyApp_Prefix.pch
ProcessPCH /Users/return/Library/Developer/Xcode/DerivedData/MyApp-cwtxjgdpsvtoyxcfpytllmzaxceb/Build/PrecompiledHeaders/MyApp_Prefix-crxrbmeralwexyefvuwvzexquuin/MyApp_Prefix.pch.pth MyApp_Prefix.pch normal armv7 objective-c com.apple.compilers.llvm.clang.1_0.compiler
cd /Users/return/Projects/iphone-MyApp
setenv LANG en_US.US-ASCII
setenv PATH "/Xcode4.2/Platforms/iPhoneOS.platform/Developer/usr/bin:/Xcode4.2/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin"
/Xcode4.2/Platforms/iPhoneOS.platform/Developer/usr/bin/clang -x objective-c-header -arch armv7 -fmessage-length=0 -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -std=c99 -Wno-trigraphs -fpascal-strings -O0 -Wreturn-type -Wparentheses -Wswitch -Wno-unused-parameter -Wunused-variable -Wunused-value -Wno-shorten-64-to-32 -DFAVOURITES_ENABLED=0 -isysroot /Xcode4.2/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk -gdwarf-2 -fvisibility=hidden -Wno-sign-conversion -mthumb "-DIBOutlet=__attribute__((iboutlet))" "-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection(ClassName)))" "-DIBAction=void)__attribute__((ibaction)" -miphoneos-version-min=3.0 -iquote "/Users/return/Library/Developer/Xcode/DerivedData/MyApp-cwtxjgdpsvtoyxcfpytllmzaxceb/ArchiveIntermediates/MyApp - Distribution/IntermediateBuildFilesPath/MyApp.build/Distribution-iphoneos/MyApp.build/MyApp-generated-files.hmap" "-I/Users/return/Library/Developer/Xcode/DerivedData/MyApp-cwtxjgdpsvtoyxcfpytllmzaxceb/ArchiveIntermediates/MyApp - Distribution/IntermediateBuildFilesPath/MyApp.build/Distribution-iphoneos/MyApp.build/MyApp-own-target-headers.hmap" "-I/Users/return/Library/Developer/Xcode/DerivedData/MyApp-cwtxjgdpsvtoyxcfpytllmzaxceb/ArchiveIntermediates/MyApp - Distribution/IntermediateBuildFilesPath/MyApp.build/Distribution-iphoneos/MyApp.build/MyApp-all-target-headers.hmap" -iquote "/Users/return/Library/Developer/Xcode/DerivedData/MyApp-cwtxjgdpsvtoyxcfpytllmzaxceb/ArchiveIntermediates/MyApp - Distribution/IntermediateBuildFilesPath/MyApp.build/Distribution-iphoneos/MyApp.build/MyApp-project-headers.hmap" "-I/Users/return/Library/Developer/Xcode/DerivedData/MyApp-cwtxjgdpsvtoyxcfpytllmzaxceb/ArchiveIntermediates/MyApp - Distribution/BuildProductsPath/Distribution-iphoneos/include" "-I”/Users/return/Library/Developer/Xcode/DerivedData/MyApp-cwtxjgdpsvtoyxcfpytllmzaxceb/ArchiveIntermediates/MyApp" -I- "-IDistribution/BuildProductsPath/Distribution-iphoneos/../three20″" "-I“/Users/return/Library/Developer/Xcode/DerivedData/MyApp-cwtxjgdpsvtoyxcfpytllmzaxceb/ArchiveIntermediates/MyApp" -I- "-IDistribution/BuildProductsPath/Distribution-iphoneos/../../three20″" -Ithree20/Build/Products/three20 -I/Xcode4.2/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/usr/include/libxml2 "-I/Users/return/Library/Developer/Xcode/DerivedData/MyApp-cwtxjgdpsvtoyxcfpytllmzaxceb/ArchiveIntermediates/MyApp - Distribution/IntermediateBuildFilesPath/MyApp.build/Distribution-iphoneos/MyApp.build/DerivedSources/armv7" "-I/Users/return/Library/Developer/Xcode/DerivedData/MyApp-cwtxjgdpsvtoyxcfpytllmzaxceb/ArchiveIntermediates/MyApp - Distribution/IntermediateBuildFilesPath/MyApp.build/Distribution-iphoneos/MyApp.build/DerivedSources" "-F/Users/return/Library/Developer/Xcode/DerivedData/MyApp-cwtxjgdpsvtoyxcfpytllmzaxceb/ArchiveIntermediates/MyApp - Distribution/BuildProductsPath/Distribution-iphoneos" -c /Users/return/Projects/iphone-MyApp/MyApp_Prefix.pch -o /Users/return/Library/Developer/Xcode/DerivedData/MyApp-cwtxjgdpsvtoyxcfpytllmzaxceb/Build/PrecompiledHeaders/MyApp_Prefix-crxrbmeralwexyefvuwvzexquuin/MyApp_Prefix.pch.pth -MMD -MT dependencies -MF /Users/return/Library/Developer/Xcode/DerivedData/MyApp-cwtxjgdpsvtoyxcfpytllmzaxceb/Build/PrecompiledHeaders/MyApp_Prefix-crxrbmeralwexyefvuwvzexquuin/MyApp_Prefix.pch.d
**clang: error: '-I-' not supported, please use -iquote instead**
**Command /Xcode4.2/Platforms/iPhoneOS.platform/Developer/usr/bin/clang failed with exit code 1**
Do you know what I should do to fix this?
UPDATE (report result after followed MacMade's suggestions)
So in the Target Build Settings:
I have nothing in Other C flags.
I have nothing in User Header Search Paths
In Header Search Paths, I have:
”$(BUILT_PRODUCTS_DIR)/../three20″
“$(BUILT_PRODUCTS_DIR)/../../three20″
./three20/Build/Products/three20
/usr/include/libxml2
but I'm not sure what to change here.
In Compiler for C/C++/Objective-C setting,
I switched from Apple LLVM compiler to LLVM GCC and get this message:
cc1obj: note: obsolete option -I- used, please use -iquote instead
cc1obj: error: -I- specified twice
cc1obj: note: obsolete option -I- used, please use -iquote instead
UPDATE (report result after followed MacMade's updated suggestions)
So I move:
”$(BUILT_PRODUCTS_DIR)/../three20″
“$(BUILT_PRODUCTS_DIR)/../../three20″
to User Header Search Paths
and it works!!!
(NOTE: I don't move ./three20/Build/Products/three20 because I see
can't find <Three20/Three20.h>
errors while building the project if I do.)
Apparently, you've set custom compiler flags for the include paths.
Go to your target's build settings and check this option:
Other C flags
If you have something in it, you may replace it by the -iquote version.
Otherwise, still in the build settings, check the value of the following options:
Header Search Paths
User Header Search Paths
Try to manipulate these values, if applicable.
Otherwise, you may also continue to use GCC as a fronted, instead of Clang.
You can do this by setting the Compiler for C/C++/Objective-C build setting to LLVM GCC instead of Apple LLVM compiler.
EDIT
Based on your edit, I can see you are using the Three20 library.
Depending on how you include that stuff, you may want to put these search paths to the User Header Search Paths, instead of Header search paths (if you include them with "" instead of <>).
Try to do this for Three20, but leave libxml here...
I tried these solutions. But i couldn't or not working for me.
I solved this problem with this way:
I deleted MuseumTests in targets
When you clicked your project in general tab on left there is targets
You can solve this problem by selecting General tab of AppNameTests and select your app from the drop down list and enable "Allow testing Host Applications APIs"
I just had this problem with an instagram lib, my mistake was that i was importing the files to a target and compiled the other target. It gave me some headaches.
Hi guys we did something that worked...
We had the same problem and it was quite easy to solve...
First you go to terminal type cd
cd (drag the whole folder where your file is in) and press return
after you are inside the folder you have to write:
pod instal and type return
something like that....
Last login: Wed Aug 21 09:19:29 on console
WozMac1:~ KrisBasque$
WozMac1:~ KrisBasque$
WozMac1:~ KrisBasque$
WozMac1:~ KrisBasque$ cd
WozMac1:~ KrisBasque$ cd /Users/KrisBasque/Documents/QUADDRO/medapp
WozMac1:medapp KrisBasque$ pod install
Analyzing dependencies
CocoaPods 0.23.0 is available.
Downloading dependencies
Using AFNetworking (1.3.1)
Installing FMDB (2.1)
Using MBProgressHUD (0.7)
Using PSStackedView (1.0)
Using Reachability (3.1.1)
Installing SDWebImage (3.3)
Using TapkuLibrary (0.2.4)
Installing libwebp (0.3.0-rc7)
Generating Pods project
Integrating client project
WozMac1:medapp KrisBasque$
The way I solved this was my making sure no other implementation files we're linked to your file. Linking interface files is fine though
This error also caused by wrong header import.
For example your class is "yourclass.h", but in wrong way if you import "yourclass.m" it is cause this error.