XCTestDefines.h is missing

Hi.
I’m trying to migrate from another CI to bitrise and my build is failing due to a missing .h file in XCTest, the error shown is:

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks/XCTest.framework/Headers/XCTest.h:32:9: ‘XCTest/XCTestDefines.h’ file not found
#import <XCTest/XCTestDefines.h>
^

I couldn’t figure out why is it failing this way.

Local reproduction: Linux / Android (docker based) stack builds

The build works fine on other machines and on another cloud based CI.

Build log

Hi @snpori,

Did you try it on your Mac with the Simulator you specified?

- SimulatorPlatform: iOS Simulator
- SimulatorDevice: iPhone 6s Plus
- SimulatorOsVersion: 9.1

I suspect iOS 9 as the target simulator iOS version might be the issue.

Thanks, on my mac it’s running with any iOS version I’m checking.
It’s also running on another CI provider.

So, it turned out to be an issue in the project itself, not related to the Simulator version at all.

I saw the project is open source (https://github.com/bluesnap/bluesnap-ios) so I registered cloned it & registered it for my own account, then I did Remote Desktop / VNC into the Build VM ( https://gist.github.com/viktorbenei/c6d4fe1e68de739dbb5f4f15de76b9db ) and tried to run the same xcodebuild command - it failed with the same issue.

The thing is, once the project is opened in Xcode.app, it works from there as well from the terminal (using Xcode’s command line tool, xcodebuild).

But if you don’t open it in Xcode.app first then it might or might not work, depends on the environment. Exactly what part of the environment I’m not sure, tried a couple of things but didn’t find the exact thing that makes it work or not work. Of course feel free to search for the error message (‘XCTest/XCTestDefines.h’ file not found and the lines around it) in Google, there are quite a few possible reasons at least for the file not found one, when it works from Xcode.app but not from the terminal / xcodebuild. You can of course also VNC/Remote Desktop into the Build VM and try to debug it yourself ( https://gist.github.com/viktorbenei/c6d4fe1e68de739dbb5f4f15de76b9db ). I tried my best but didn’t have enough time, especially as it worked after opening the project in Xcode.app via a Script :slight_smile:

Historically these kind of issues (works once the project is opened in Xcode.app, but does not work with xcodebuild without opening it in Xcode.app first) are related to some kind of Scheme issue, e.g. when the Scheme is not marked as shared, but in your case the Scheme you specified seems to be marked as shared and those issues are usually reproducible in every environment before opening the project in Xcode.app first, while in your case it happens in some environments but not in others.

My guess right now is that you might have .gitignored an important part of the scheme (schemes are stored as directories - http://devcenter.bitrise.io/ios/frequent-ios-issues/#xcode-scheme-not-found -> “This change should be reflected in your git repository, under you project / workspace (which is actually a directory, just seems like a file in Finder): *.xcodeproj OR *.xcworkspace/xcshareddata/xcschemes/SchemeName.xcscheme”).
We saw issues related to things like these before, e.g. that part of the Scheme was committed into the repo but then it got .gitignored, in which case the state before the .gitignore is still in the repo, but not the updates, e.g. other files related to the scheme / which reference the scheme, leaving it in a broken state. Usually opening the project in Xcode.app helps as Xcode.app can auto generate these Scheme related files - in fact if the Scheme is not marked as shared then it’s generated exactly when the project is opened in Xcode.app the first time.

Of course it can be related to something else too, but in any case this is not Bitrise related. It’s related to something in the environment (our build VMs are clean macOS installs with Xcode.app and a couple of tools preinstalled, and that’s pretty much all - you’d get the same after doing a clean install; you can find all the scripts we use for the prepare of the VM images at: https://github.com/bitrise-io/osx-box-bootstrap ) in combination with your xcode project.

Anyway, if you have some time feel free to remote desktop / vnc into the build VM and try to debug exactly what causes the issue.


The solution is quite simple: open the workspace file in Xcode.app

You can do that with a Script step, right before the first Xcode step:

#!/usr/bin/env bash
set -ex

open "$BITRISE_PROJECT_PATH"

That’s all. Once the project is opened in Xcode.app all other steps which use Xcode’s command line tool (xcodebuild) will work. Of course this has a slight performance penalty, but in general it should be fine if your build is not RAM constraint.

I hope this helps, if you’d have any questions just let me know! :slight_smile:

P.S.: I’ll try to see which files change when the project is opened in Xcode.app once I have some time :wink:

Finally managed to reproduce the issue in every environment. All you have to do is to remove Xcode’s build cache dir (DerivedData) and the next time you compile the project using xcodebuild you’ll get the same error.

A simple script to reproduce the issue:

#!/usr/bin/env bash
set -ex

# create a temporary directory
test_repo_pth="/tmp/test-repo-$(date +%s)"
mkdir "$test_repo_pth"
cd "$test_repo_pth"
git clone https://github.com/bluesnap/bluesnap-ios.git .

# delete Xcode's DerivedData build cache dir
rm -rf ~/Library/Developer/Xcode/DerivedData

# now try to run the tests
xcodebuild "-workspace" "BluesnapIOS.xcworkspace" "-scheme" "BluesnapSDKExample" "build" "test" "-destination" "platform=iOS Simulator,name=iPhone 6s Plus,OS=latest"

If you’d have any questions just let me know! :wink:

Hello Viktor. Thanks a lot for the investigation.
It does seems that information DerivedData is the root cause of this problem, however I don’t feel comfortable with the need to run xcode UI just to fix that. Also adding xcode user specific settings to git is not an option.

So after digging a bit I figured out that running xcodebuild test without “build” and specifiying -derivedDataPath solves the issue.
I modified the build line in your script and now it builds correctly and running the tests.

#!/usr/bin/env bash
set -ex

# create a temporary directory
test_repo_pth="/tmp/test-repo-$(date +%s)"
mkdir "$test_repo_pth"
cd "$test_repo_pth"
git clone https://github.com/bluesnap/bluesnap-ios.git .

# delete Xcode's DerivedData build cache dir
rm -rf ~/Library/Developer/Xcode/DerivedData

xcodebuild  test -scheme "BluesnapSDKExample" -derivedDataPath "DD" -resultBundlePath "DD/iostests_results/" -enableCodeCoverage "YES" -configu
ration "Debug" ENABLE_TESTABILITY=YES -destination "platform=iOS Simulator,name=iPhone 6s Plus,OS=latest" -workspace "BluesnapIOS.xcworkspace" 
ONLY_ACTIVE_ARCH=YES

xcodebuild  build -scheme "BluesnapSDKExample" -derivedDataPath "DD" -configuration "Debug" ENABLE_TESTABILITY=YES -destination "platform=iOS Simulator,name=iPhone 6s Plus,OS=latest" -workspace "BluesnapIOS.xcworkspace" ONLY_ACTIVE_ARCH=YES

==
~

so after all it does seems to be the way bitrise runs the build :wink:
I think that bitrise should use a derivedDataPath argument to xcodebuild by default,
actually if you run your script with only “test” it might work as well.

Seems like I should change the workflow to run xc test and then xc build/archive.
What do you think?

PS also look at this this:

it says: " Note: build-for-testing and test-without-building provide support for continuous integration systems."
you should really look into this to make sure bitrise does good iOS CI.

Hello

1 Like

Xcode does not specify a special Derived Data path either, that’s exactly why the path was ~/Library/Developer/Xcode/DerivedData, as that’s the default path :wink: In fact we saw issues in the past related to specifying a different DerivedData path, although that might have been just because certain custom user scripts were looking for the default dir…

If you need it you can specify additional xcodebuild options for the Xcode steps, see e.g. - https://github.com/bitrise-io/steps-xcode-test/blob/fc3e3afff10919d1618dcd9b0684d99cf0d89c52/step.yml#L147 (Xcode test step - xcodebuild_test_options option). Of course this should not be required, still not sure why your project can’t work with an empty DerivedData path, never seen that with any other project before. But in your case, if you don’t want to fire up Xcode.app I’d suggest you to use this option to specify a derived data path you’re happy with.

That’s if you want to separately build and run the test. But in your workflow it doesn’t seem you want to do that, you want to build and run the test right after that it seems. For that test is still the default action (build & run the test) AFAIK. So Bitrise is already a good iOS CI :wink: And it’s not even bitrise related, you can use any other step or script, our Xcode Test step uses the most error proof options we found during the years, but no single option works for everyone, that’s why the step has additional options etc. :wink: That said, as I mentioned above, this was never an issue with any other project before :slight_smile:

From the same link/docs, How do I run unit tests from the command line? section:

To build and run unit tests from the command line, execute the following command in Terminal: xcodebuild test ...

To build unit tests without running them from the command line, execute the following command in Terminal: xcodebuild build-for-testing ...

To run unit tests without building them from the command line, execute any of the following command in Terminal: xcodebuild test-without-building ...

xcodebuild test ... is pretty much just a shorthand to do both if you want to build and run the test, the other commands do only part of it.

@viktorbenei @snpori I recently had this same issue, and I think it is related. I supplied a custom framework search path at the project-level build settings, which is seems you’re doing as well for BluesnapSDKExample. Once I removed that custom search path and applied it on a per-target basis, this issue went away.

In your project, you’ve added $(BUILT_PRODUCTS_DIR) as a search directory recursively.

1 Like

Ohh, that’s definitely a great point, thanks for sharing @allenhumphreys, I’m sure this’ll help others in the future! :wink: