Setting Up A C++ Project With CMake

Complete repository for the lazy time-sensitive here.

CMake is a useful build tool for use (at least for my purposes) with C++. It is able to generate scripts to build your program on a variety of different platforms, and in the end, it makes a developer’s life easier.

I have spent the last few days struggling with rearranging my build process with CMake.

All in all, I like it.

It seems like a great tool, however I feel the project could use more straightforward documentation on setting up a project that is split into several parts. Digging into the CMake documentation can be intimidating at first, hopefully I can help ease your pain.

The project I am trying to set up has three separate components.  An executable, a library, and a Google Test executable. Our goal will be to achieve the following file structure:

First I will add a top level CMakeLists.txt file:

CMakeLists.txt
 
  1. cmake_minimum_required(VERSION 3.6)
  2. project(CMakeSetup)

 

Then I will add a subdirectory called library with the following files

library/Library.h
 
  1. #ifndef CMAKESETUP_LIBRARY_H
  2. #define CMAKESETUP_LIBRARY_H
  3. class Library
  4. {
  5. public:
  6.   int GetTen();
  7. };
  8. #endif //CMAKESETUP_LIBRARY_H

 

library/Library.cpp
 
  1. #include "Library.h"
  2. int Library::GetTen()
  3. {
  4.   return 10;
  5. }
  6.  

 

library/CMakeLists.txt
 
  1. add_library(library STATIC Library.cpp) 
  2. set_property(TARGET library PROPERTY CXX_STANDARD 14)

 

At this point we can revisit our top level CMakeLists.txt file and add an additional line:

CMakeLists.txt
 
  1. add_subdirectory(library)



This will tell CMake to process the CMakeLists.txt file in the library directory.

Next create another directory application with the following files:

application/main.cpp
 
  1. #include <iostream>
  2. #include "../library/Library.h"
  3. int main()
  4. {
  5.   Library lib {};
  6.   std::cout << "Library::GetTen returns " << lib.GetTen();
  7. }

 

application/CMakeLists.txt
 
  1. add_executable(application main.cpp)
  2. set_property(TARGET application PROPERTY CXX_STANDARD 14)
  3. target_link_libraries(application library)



Everything here should be pretty straightforward.

This tells CMake to produce an executable target named application from the listed files (main.cpp). We tell CMake we would like application to be built with C++14 support. Lastly, we tell CMake to link application with our library.

Revisiting our top level CMakeLists.txt file, we can add another add_subdirectory() command:

CMakeLists.txt
 
  1. add_subdirectory(application)

 

At this point we can try building our application. Personally I use CLion which takes care of a lot of the CMake commands for me. However, from the console you should be able to generate the makefiles on the command line.

In your top level directory of the project, you can execute something like this:

 
 
  1. mkdir debug
  2. cd debug
  3. cmake -DCMAKE_BUILD_TYPE=Debug -"CodeBlocks - Unix Makefiles" ../



This will generate the makefiles in the debug folder. Note I am using CMake on Ubuntu Linux and you may need to use a different generator (instead of CodeBlocks – Unix Makefiles) depending on your platform.

You can read more about generators at https://cmake.org/cmake/help/v3.7/manual/cmake-generators.7.html to figure out what one is appropriate for your system.

Going back to your terminal, you can initiate the build process:

 
 
  1. cmake --build . --target all

 

At this point you should have a working executable in debug/application.

Next we will begin to integrate Google Tests into our project. Go to https://github.com/google/googletest/tree/release-1.8.0 and download a zip archive of the the 1.8 release.

In the root directory of our project, create a tests subdirectory and a tests/lib subdirectory.

Extract the contents of the zip file. Google Tests ships with a CMakeLists.txt file so we can simply include it into our root directory CMakeLists.txt file:

 
 
  1. add_subdirectory(tests/lib)



Next we will add a test. Create a main.cpp file in tests:

tests/main.cpp
 
  1. #include <gtest/gtest.h>
  2. #include "../library/Library.h"
  3. TEST(LibraryGetTen, ReturnsTen) {
  4.   Library lib {};
  5.   ASSERT_EQ(lib.GetTen()10);
  6. }
  7. int main(int argc, char **argv) {
  8.   testing::InitGoogleTest(&argc, argv);
  9.   return RUN_ALL_TESTS();
  10. }



Now add a CMakeLists.txt file under tests to build the test executable:

tests/CMakeLists.txt
 
  1. add_executable(tests main.cpp)
  2. set_property(TARGET application PROPERTY CXX_STANDARD 14)
  3. target_link_libraries(tests gtest gtest_main library)



And back in your root directory CMakeLists.txt file add:

 
 
  1. add_subdirectory(tests)



Our complete project setup should look like this:

Our project is now all ready to be rebuilt.

From the debug directory we created, execute the following cmake commands again:

 
 
  1. cmake -DCMAKE_BUILD_TYPE=Debug -"CodeBlocks - Unix Makefiles" ../
  2. cmake --build . --target all

 

You can now navigate to the tests directory under debug and run the test executable:

And there is is.  A complete CMake setup with three distinct parts! Please leave me feedback if anything is unclear to you, my hope is this tutorial helps you in your journey with CMake!

Leave a Reply

Your email address will not be published. Required fields are marked *