I'm creating an application using Swift Package Manager and I need to know the configuration of which the project was built under, i.e Debug or Release. I'm trying to stay away from using the .xcodeproj file. Please, someone let me know if this is possible. I'll paste a simple example below, but just know if there's a better way to handling configuration besides the code below, please submit as answer.
Example:
// main.swift
#if DEBUG
print("Im in Debug!!")
#else
print("Im in Release!!")
#endif
As of Swift 3.1, the Package Manager doesn't offer the option to customize build settings in the Package.swift file (this feature is part of the team's roadmap for Swift 4). However, you can use the -Xswiftc flag to pass custom build settings to the compiler when you invoke swift build or swift test.
To set the DEBUG flag in debug mode, invoke swift build like this:
swift build --configuration debug -Xswiftc "-D" -Xswiftc "DEBUG"
And for release builds you would do the usual:
swift build --configuration release
There is also -Xlinker and Xcc to pass flags to the linker and C compiler, respectively.
Related
We are trying to use xcodebuild to build our frameworks, instead of using manual Xcode IDE running buttons. The issue is that in our framework we use ACTIVE_COMPILATION_CONDITIONS, which have several values. Those values are then used to check at runtime if the particular framework is integrated, like checking for Sentry:
#if SENTRY_AVAILABLE
import Sentry
#endif
The problem is that, for some builds, we need to override our project settings, specifically ACTIVE_COMPILATION_CONDITIONS. However, after the following script successfully executes, the Xcode still does not override our provided ACTIVE_COMPILATION_CONDITIONS with defined in the project.
The script:
xcodebuild -workspace project.xcworkspace -scheme SDKNR1 ONLY_ACTIVE_ARCH=NO
EXCLUDED_ARCHS=arm64 ACTIVE_COMPILATION_CONDITIONS=SENTRY_AVAILABLE -configuration
release -derivedDataPath $PROJECT_DIR/../simulators/SDKNR1 -sdk iphonesimulator
ENABLE_BITCODE=YES BITCODE_GENERATION_MODE=bitcode OTHER_CFLAGS="-fembed-bitcode" clean build
As you can see we define ACTIVE_COMPILATION_CONDITIONS=SENTRY_AVAILABLE, however it will not override target's (SDKNR1) project settings. Suppose, SDKNR1 does not have any ACTIVE_COMPILATION_CONDITIONS. We expected that xcodebuild command would override target's ACTIVE_COMPILATION_CONDITIONS and would include SENTRY_AVAILABLE
Would welcome any ideas, or perhaps it is not possible?
The proper build setting key is "SWIFT_ACTIVE_COMPILATION_CONDITIONS"
You can double check this by using the command and verifying the key exists:
xcodebuild -showBuildSettings <project/scheme/target/configuration flags>
Results from -showBuildSettings (truncated, for RELEASE_CONDITION2 set in the Xcode project settings for release build [for some reason debug wont show]):
.....
SUPPORTS_TEXT_BASED_API = NO
SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE_CONDITION2
SWIFT_COMPILATION_MODE = wholemodule
.....
In build log, you should see (as example here, setting RELEASE_CONIDTION2, also note that the ACTIVE_COMPLIATION_CONIDTIONS gets translated into -D parameters for swiftc):
Build settings from command line:
.....
SDKROOT = iphoneos14.5
SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE_CONDITION2
.....
CompileSwiftSources normal arm64 com.apple.xcode.tools.swift.compiler .....
.....
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc <most compiler options removed> -DRELEASE_CONDITION2
Above is all with Xcode 12.5
I'm trying to build RxSwift using Bazel in order to be able to use it in a dummy project as a dependency, for the sake of getting to know it a bit.
We don't use the latest RxSwift version at the moment, but version 4.4.2.
After reading through the docs, I've come to the point where I successfully add the specific release to my workspace as an http_archive, and supply my own BUILD file to build that external dependency RxSwift.
You can check out the BUILD file and WORKSPACE file here:
https://gist.github.com/daneov/487444109c703087862d830a3445ee86
Running
bazel build #rx_swift//:RxSwift
yields a Swift compilation error:
No such module: RxAtomic
So, this shows that my I'm missing something with regards to exposing the Objective-C module to a Swift library.
I currently supply these options to the objc_framework:
enable_modules = 1,
alwayslink = 1,
module_name = "RxAtomic"
Thanks in advance for any thoughts/pointers!
You should call bazel build with --experimental_objc_enable_module_maps option
I created a Swift library with swift package init --type library and generated an Xcode project with swift package generate-xcodeproj.
Now I'm trying to run the Test scheme in Xcode. It prints following error:
Module '<title>' was not compiled for testing
However when I run swift build and swift test in terminal, it works fine.
I have ENABLE_TESTABILITY set to YES in all of the targets. I didn't change anything in the project except this. How can I make Xcode perform unit testing?
You need to set the "Enable Testability" to Yes in build setting over your "Main Target"
I was having this issue today, it seems like #testable cannot be used with projects generated by Swift Package Manager.
Removing #testable from my import statements solved this issue. Of course, this means we can only test the public interface of our modules.
Xcode -> Product -> Scheme -> Edit Scheme
Select the Info tab.
Set the following -
Build Configuration: Debug
Add a check mark to Debug executable
Tested with Xcode 12.4(12D4e) and iOS 14.1 deployment target.
I am developing a server in Swift and using the Swift Package Manager. And find it convenient when doing my development on my Mac OS system to generate a Xcode project to use Xcode as my IDE (i.e., From time to time, my package dependencies have to be updated. I've been using swift package generate-xcodeproj to do this. My problem comes in at this point-- I have created some settings in Xcode. E.g., I've set a DEBUG flag, and I have a .plist file that is in the Copy Files Phase. These get lost when I regenerate the Xcode project. It seems I cannot simply use swift package update because sometimes files change in the dependencies and these don't get propagated to the Xcode project.
What I'd like is a means to separately establish Xcode settings in a file outside of Xcode, that can be imported into Xcode when I do the swift package generate-xcodeproj. I have not seen a way to do this.
A related question is: When I do a swift build I'd like those same build settings to be used.
Suggestions?
I can't help with Copy Files Phase.
However I have just been toying with conditional compilation, like this:
swift package generate-xcodeproj --xcconfig-overrides Sandbox.xcconfig
Sandbox.xcconfig
FLAG_SANDBOX = -DSANDBOX
OTHER_SWIFT_FLAGS = $(FLAG_SANDBOX)
This creates an Xcode project where SANDBOXis definded.
This can be used in swift code like this
#if SANDBOX
print("sandbox")
#else
print("production")
#endif
I would use a script or a makefile to import your settings into the generated Xcode project, each time you regenerate it. You can use xcodeproj rubygem.
See an example script.
Regarding using your settings in swift build, can you give an example of such a setting? In general, a makefile can read your settings file and pass the corresponding parameters to swift build.
Based on #vadim's answer above, here's a xcodeproj rubygem solution to the first part of my question:
#!/usr/bin/ruby
# Tweak the .xcodeproj after creating with the swift package manager.
# Resources:
# https://stackoverflow.com/questions/41527782/swift-package-manager-and-xcode-retaining-xcode-settings/41612477#41612477
# https://stackoverflow.com/questions/20072937/add-run-script-build-phase-to-xcode-project-from-podspec
# https://github.com/IBM-Swift/Kitura-Build/blob/master/build/fix_xcode_project.rb
# http://www.rubydoc.info/github/CocoaPods/Xcodeproj/Xcodeproj%2FProject%2FObject%2FAbstractTarget%3Anew_shell_script_build_phase
# http://www.rubydoc.info/github/CocoaPods/Xcodeproj/Xcodeproj/Project/Object/AbstractTarget
# https://gist.github.com/niklasberglund/129065e2612d00c811d0
# https://github.com/CocoaPods/Xcodeproj
# https://stackoverflow.com/questions/34367048/how-do-you-automate-do-copy-files-in-build-phases-using-a-cocoapods-post-insta?rq=1
require 'xcodeproj'
path_to_project = "Server.xcodeproj"
project = Xcodeproj::Project.open(path_to_project)
# 1) Add Copy Files Phase for Server.plist to the Products directory for Server target
target = project.targets.select { |target| target.name == 'Server' }.first
puts "Add Copy Files Phase to #{target}"
phase = target.new_copy_files_build_phase()
# Contrary to the docs (see http://www.rubydoc.info/github/CocoaPods/Xcodeproj/Xcodeproj/Project/Object/PBXCopyFilesBuildPhase) I believe this is not a path, but rather a code, e.g., 16 indicates to copy the file to the Products Directory.
phase.dst_subfolder_spec = "16"
fileRef = project.new(Xcodeproj::Project::Object::PBXFileReference)
fileRef.path = 'Server.plist'
phase.add_file_reference(fileRef)
# 2) Add in script phase for testing target-- because I haven't figured out to get access to the Products directory at test-run time.
target = project.targets.select { |target| target.name == 'ServerTests' }.first
puts "Add Script Phase to #{target}"
phase = target.new_shell_script_build_phase()
phase.shell_script = "cp Server.plist /tmp"
# 3) Add in DEBUG flag
# A little overkill, but hopefully appending a DEBUG flag in the Debug configuration for each target doesn't hurt it.
project.targets.each do |target|
puts "Appending DEBUG flag to #{target}"
if target.build_settings('Debug')['OTHER_SWIFT_FLAGS'].nil?
target.build_settings('Debug')['OTHER_SWIFT_FLAGS'] = ""
end
target.build_settings('Debug')['OTHER_SWIFT_FLAGS'] << '-DDEBUG'
end
project.save()
in my iPhone project I am using some inline asm, which is excluded if the target architecture is the device and not the simulator.
Since some of the inline asm code is arm only and not thumb I need to specify the c flag -marm when compiling it for the iPhone, since it otherwise trys to compile the code with the thumb instructions.
And here is the problem if I enter the -marm flag in the file specific build setting, gcc outputs an error if I compile for the simulator:
cc1obj: error: unrecognized command line option "-marm"
Is there a way to pass this option only if the target architecture is arm?
I know you can do it with the global c flags, but I dont want to compile my whole project with the -marm flag. I want only a few .m files to be -marm.
Thanks and greetings, Kim
OK I found a solution for the issue.
Here is the comment I added to the code:
// the asm code only works with arm not with thumb,
// so if you compile it and gcc trys to compile it as thumb
// you will get an error.
// to make gcc compile the file as arm and not thumb you need
// to add -marm to the files compiler config (select the file
// and press cmd + i and select the build tab and enter there
// the problem is that if you try to compile for the simulator
// it will fail, because intel gcc doesnt know the flat -marm.
// To solve this add a new "User defined setting" in your targets
// build settings with the name use_marm and give it the value ""
// then add a build setting condition and select Any iPhone OS
// Device and give it the value "-marm"
// In your file's compiler flags add $use_marm
// If you build for the device it will add -marm and if you
// build for the simulator it wont.