xcodebuild: how to really change its build path
This is short one-solution-post that is a longer version of my answer to this StackOverflow topic: Separate build directory using xcodebuild.
Background: I build my tests from both Xcode and command line.
Problem: If I run my tests with Command + U and then I run them with make test, the build artifacts of test suite built from Xcode become overriden so that next time I run Command + U my test target will be built from scratch again. The benefits of incremental compilation are lost for both modes as both kinds of test runs override each other.
Solution: Completely isolate build artefacts that are created by command-line builds from corresponding build artefacts created by Xcode when tests are run from within Xcode.
My naive approach to solve this problem was to redirect xcodebuild to another CONFIGURATION_BUILD_DIR like:
xcodebuild ... CONFIGURATION_BUILD_DIR=./Build-command-line
However it turned out that xcodebuild had some build settings that were not controlled by this variable i.e. Xcode was still building some of its artefacts to its default ./Build folder. So I needed to investigate on details of xcodebuild -showBuildSettings to understand how to make xcodebuild to build everything to desired ./Build-command-line folder. I used
xcodebuild -scheme MyScheme -showBuildSettings | grep Build\/
to find all build settings that correspond to all build paths and by trial-end-error I found "the generative" build settings that are enough to redirect all build artefacts produced by xcodebuild to custom folder.
I ended up using the following command:
BUILD_DIR=./Build-command-line
DERIVED_DATA_DIR=$(BUILD_DIR)/DerivedData
xcodebuild -project MyProject.xcodeproj \
-IDEBuildOperationMaxNumberOfConcurrentCompileTasks=`sysctl -n hw.ncpu` \
-scheme MyScheme \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 6S Plus,OS=latest' \
-xcconfig command-line-build.xcconfig \
-derivedDataPath $(DERIVED_DATA_DIR) \
test
Where command-line-build.xcconfig is:
/// This xcconfig is used to make Xcode build all of its artefacts to a
/// custom folder. Use it for command line builds so that their caches
/// do not interfere with the caches of normal builds from inside Xcode.
///
/// To make most sense of the following configuration you should also redirect
/// path to derived data using xcodebuild parameter:
/// xcodebuild ...-derivedDataPath $(DERIVED_DATA_DIR)...
/// where $(DERIVED_DATA_DIR) also points to Build-command-line/DerivedData
///
/// Tested against Xcode 7.3.1 (7D1014)
///
/// Source: xcodebuild: how to really change its build path
/// http://stanislaw.github.io/2016/02/28/xcodebuild-how-to-really-change-its-build-path.html
HERE_BUILD=$(SRCROOT)/Build-command-line
HERE_INTERMEDIATES=$(HERE_BUILD)/Intermediates
/// Paths
/// the following paths are enough to redirect everything to $HERE_BUILD
MODULE_CACHE_DIR = $(HERE_BUILD)/DerivedData/ModuleCache
OBJROOT = $(HERE_INTERMEDIATES)
SHARED_PRECOMPS_DIR = $(HERE_INTERMEDIATES)/PrecompiledHeaders
SYMROOT = $(HERE_BUILD)/Products
Having this setup I can now use both Command + U and make test (which is make test_unit && make test_integration && make test_functional done by Make) - they are now built in two separate directories and that makes a very good speed-up in build time of both.
P.S. Of course xcodebuild's build settings are subject to change however as of Xcode Version 7.3.1 (7D1014) this solution works very well.