A guide for using CMake Generators to support other IDEs.

Some times we might be working in a team, and that team has members who prefer to use different IDEs or who develop on different platforms. In these cases we can leverage CMake’s generators.
This is a short guide on using a generator for Xcode and adding a second test file to break up testing.

Directory Structure

This is the structure that we start with.

❯ tree
├── CMakeLists.txt
├── README.md
├── src
│   ├── CMakeLists.txt
│   ├── Example.cpp
│   └── Example.hpp
└── tests
    ├── CMakeLists.txt
    └── lib-tester.cpp

Using the Generator

There are a number of generators that can be used. They are documented here

There are a couple of ways to create the build directory. I am naming mine build-xcode here because by default, vscode creates a directory named build. Xcode and vscode can not use the same build directory.

It is generally recommended that the build directory not exist within the project directory when using CMake.

I am not following that advice here for simplicity.

  1. Using the B flag
❯ cmake -Bbuild-xcode -GXcode
-- The CXX compiler identification is AppleClang 13.0.0.13000029
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found GTest: /usr/local/lib/cmake/GTest/GTestConfig.cmake (found version "1.11.0")  
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/aspann/Documents/Development/cpp/cmake-c++-template/build-xcode
  1. Create the build directory by hand.
❯ mkdir build-xcode && cd $_ && cmake .. -GXcode
-- The CXX compiler identification is AppleClang 13.0.0.13000029
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found GTest: /usr/local/lib/cmake/GTest/GTestConfig.cmake (found version "1.11.0")  
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/aspann/Documents/Development/cpp/cmake-c++-template/build-xcode

This gives us the following.

❯ tree
├── C++-CMake-Library-Development.png
├── CMakeLists.txt
├── README.md
├── build-xcode
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   │   ├── 3.22.1
│   │   │   ├── CMakeCXXCompiler.cmake
│   │   │   ├── CMakeDetermineCompilerABI_CXX.bin
│   │   │   ├── CMakeSystem.cmake
│   │   │   └── CompilerIdCXX
│   │   │       ├── CMakeCXXCompilerId.cpp
│   │   │       ├── CompilerIdCXX
│   │   │       ├── CompilerIdCXX.build
│   │   │       │   └── Debug
│   │   │       │       └── CompilerIdCXX.build
│   │   │       │           ├── CompilerIdCXX-all-non-framework-target-headers.hmap
│   │   │       │           ├── CompilerIdCXX-all-target-headers.hmap
│   │   │       │           ├── CompilerIdCXX-generated-files.hmap
│   │   │       │           ├── CompilerIdCXX-own-target-headers.hmap
│   │   │       │           ├── CompilerIdCXX-project-headers.hmap
│   │   │       │           ├── CompilerIdCXX.hmap
│   │   │       │           ├── Objects-normal
│   │   │       │           │   └── x86_64
│   │   │       │           │       ├── CMakeCXXCompilerId.d
│   │   │       │           │       ├── CMakeCXXCompilerId.dia
│   │   │       │           │       ├── CMakeCXXCompilerId.o
│   │   │       │           │       ├── CompilerIdCXX.LinkFileList
│   │   │       │           │       └── CompilerIdCXX_dependency_info.dat
│   │   │       │           ├── Script-2C8FEB8E15DC1A1A00E56A5D.sh
│   │   │       │           └── all-product-headers.yaml
│   │   │       ├── CompilerIdCXX.xcodeproj
│   │   │       │   └── project.pbxproj
│   │   │       ├── XCBuildData
│   │   │       │   ├── 3482802cdf712446fc88789941bc3089-buildRequest.json
│   │   │       │   ├── 3482802cdf712446fc88789941bc3089-desc.xcbuild
│   │   │       │   ├── 3482802cdf712446fc88789941bc3089-manifest.xcbuild
│   │   │       │   ├── 3482802cdf712446fc88789941bc3089-targetGraph.txt
│   │   │       │   ├── BuildDescriptionCacheIndex-b1f97e8c6205ba582a37b4bbb8d2d944
│   │   │       │   └── build.db
│   │   │       └── tmp
│   │   ├── CMakeOutput.log
│   │   ├── CMakeTmp
│   │   ├── TargetDirectories.txt
│   │   └── cmake.check_cache
│   ├── CMakeScripts
│   │   └── ReRunCMake.make
│   ├── CTestTestfile.cmake
│   ├── cmake_install.cmake
│   ├── mylib.xcodeproj
│   │   ├── project.pbxproj
│   │   └── project.xcworkspace
│   │       └── xcshareddata
│   │           └── WorkspaceSettings.xcsettings
│   ├── src
│   │   ├── CMakeFiles
│   │   ├── CTestTestfile.cmake
│   │   └── cmake_install.cmake
│   └── tests
│       ├── CMakeFiles
│       ├── CTestTestfile.cmake
│       └── cmake_install.cmake
├── src
│   ├── CMakeLists.txt
│   ├── Example.cpp
│   └── Example.hpp
└── tests
    ├── CMakeLists.txt
    └── lib-tester.cpp

23 directories, 46 files

Opening the project in Xcode

The fastest way to do this is from in the build directory.

❯ open mylib.xcodeproj

This should give you something like this.

Opened XCode Project

  1. Open the bottom panel and make sure that All Output is selected.
  2. Make sure you have selected Test1Tester program as your target at the top centre of the editor and run the program to generate the output. You may need to run the program twice. Some oddity with Xcode.

This should give the following result.

Xcode Execute Test1Tester

Adding a New Test Program to our project.

It is often best to break your tests down into more manageable files. So I will make a new file for testing. This will done outside Xcode.

Update the CMakeLists.txt under tests

To keep things simple I am just adding a single program file lib-tester2.cpp to the tests directory and then making CMake aware of the file and creating the new target with the following commands. You can check the actual config here

 
# Specify second test program and source files
set(test2 Test2Tester)
set(test2src lib-tester2.cpp)
 
# Specify executable for second tester
add_executable(${test2} ${test2src})
 
# Specify target for second test program
target_link_libraries( ${test2} LINK_PUBLIC ${GTEST_LIBRARIES} ${CMAKE_PROJECT_NAME})

Telling Xcode about the changes.

For this we simply need to rerun cmake with the same command we used to generate the Xcode project.

Here I am running the command from build-xcode

cmake .. -GXcode

Cmake will update the project files and xcode should show the following changes. These changes are marked with small red circles. There should be three of them.

Xcode test2Tester Added to Project

Running the new Test Program.

Just select test2Tester as the new target at the top of the editor as shown in the image above and running the target.

This will give you the following:

Xcode test2Tester Run target

If you look closely you will see that we are now running Test1.Number which is different to our previous test program which was mTESTThree.Number

Closing Comment

I put this together just to show that using CMake can help us have a consistent code base while developers might use different IDEs.


See also