Building with CMake

[NOTE: THIS IS A WORK IN PROGRESS AND NOT SUITABLE FOR USE YET]

So you would like to use CMake (and CTest) to handle your test builds? Good choice! Unity loves working with CMake and friends.

For the purpose of this simple example, we're going to organize our code in the same default as we do in our make and other intros. Our project's directory structure will look something like this:

  • build - where all temporary stuff goes
  • src - where we have all our source code for release (and to be tested)
  • test - where we have all our unit tests
  • unity - where we have copied the latest copy of the Unity project

CMake likes to think of the world as lists. In each directory, it keeps a file called CMakeLists.txt. This file is a text file to reinforce that you're welcome to edit this with your handy dandy text editor. It contains instructions for building the things in that directory. Let's start with the one in our project root:

./CMakeLists.txt

project("My Awesome Tested Project" C) 
cmake_minimum_required(VERSION 3.0)

# Let's tell it a bit about our environment
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)

# We declare a variable to select what to build set
(TARGET_GROUP production CACHE STRING "What Should I Build?")

# Modules that exist in both our release and our unit tests, are added now
  add_subdirectory(src) 

if(TARGET_GROUP STREQUAL release)
# If we had any files that ONLY were compiled into our release, for example startup code,
  # it's really handy to put it here. 
elseif(TARGET_GROUP STREQUAL test)
# Our tests need to let CTest know we're running tests 
  include(CTest)

# It also needs to understand we have code in source and test directories 
add_subdirectory(unity) 
  add_subdirectory(test) 
else() 
  message(FATAL_ERROR "I don't know the TARGET_GROUP you gave me!")
endif()

As you can see, it's fairly straightforward. It gives us access to a couple of target groups, release and test. These include the directories required to perform those actions. When it's a test, it also includes CTest, so that it can manage that process for us. You can see that it would be fairly easy to add additional subdirectories to either or both builds.

Let's look at the list in our unity directory. Because this just contains Unity itself, this should be fairly straightforward.

unity/CMakeLists.txt

add_library(unity STATIC src/unity.c)
 
target_include_directories(unity PUBLIC src)

The first line tells us that we're adding Unity as a library to our build. The second tells us where we can find the include directories for our friend Unity, which is Unity's src directory.

Our list in our source directory is very similar, actually, but we get to use a wildcard to pull in all the source files present:

src/CMakeLists.txt

file(GLOB SOURCES ./*.c)

add_executable(release ${SOURCES})

target_include_directories(release PUBLIC ${CMAKE_CURRENT_LIST_DIR})

One thing we should note about operating this way. CMake doesn't know when to add more release files to your list. You must explicitly call cmake again in order to get it to notice any files that you add to your source directory. THEN it will add that file to your makefile and you'll be good to go. 

Finally, let's look at the list in our test directory:

test/CMakeLists.txt

add_executable(test_meh_app test_meh.c)
 
target_link_libraries(test_meh_app src unity)
 
add_test(test_meh test_meh_app)

Wow. This needs to be made more generic. Listing each file and what it depends on sounds really tedious.