This is short one-solution-post that is 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
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
/// 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.
See also my post: Test Automation for iOS.