C++ – CMake with gmock

c++cmake

I just want to make sure that my understanding about CMakeLists.txt is correct. My dummy project structure:

|-+ dummy
|-+ CMakeLists.txt
    |-+ src
        |-- CMakeLists.txt
        |-- Converter.cpp
        |-- Converter.hpp
        |-- main.cpp
    |-+ tests
        |-- CMakeLists.txt
        |-- Converter_ut.cpp
    |-+ thirdparty
        |-+ gmock-1.7.0

My goal is to create build process with CMake. This is my first attempt so I assume that there are some mistakes. It works but I am not sure if I understand everything correctly and I would be thankful if you could share with some comments / suggestions.

dummy/CMakeLists.txt

cmake_minimum_required (VERSION 2.8.11)
project (SUB)

add_subdirectory (src)
add_subdirectory (tests)
  1. cmake_minimum_required is pretty self-explanatory,
  2. project (SUB) sets project variables like ${SUB_SOURCE_DIR} and ${SUB_BINARY_DIR},
  3. add_subdirectory, tells CMake to go and process CMakeLists.txt in the following directories

src/CMakeLists.txt

add_library (Sub
    main.cpp
    Converter.cpp)

target_include_directories (Sub PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

# Executable
add_executable (converter
    Converter.cpp)

target_link_libraries (converter Sub)
  1. add_library, creates library called "Sub" from two source files,
  2. target_include_directories, tells the compiler where are the header files for "Sub" library (is that "PUBLIC" really needed here?),
  3. add_executable, creates "converter" executable from Converter.cpp (why main.cpp is not needed here?),
  4. target_link_libraries, links "Sub" library with "converter" executable

tests/CMakeLists.txt

# GMOCK
set (GMOCK_DIR "../thirdparty/gmock-1.7.0")
add_subdirectory(${GMOCK_DIR} ${CMAKE_BINARY_DIR}/gmock)
include_directories(SYSTEM ${GMOCK_DIR}/include ${GMOCK_DIR}/gtest/include)

# Executable
add_executable (tests
    Converter_ut.cpp)

target_link_libraries (tests gmock_main Sub)
  1. set (GMOCK_DIR …), sets local variable "GMOCK_DIR" with my gmock folder location,
  2. add_subdirectory, tells CMake to jump into gmock location and run their CMakeLists.txt, what is the second argument? {CMAKE_BINARY_DIR}/gmock?
  3. add_executable, creates second executable file
  4. target_link_libraries, links gmock_main library with second executable, "Sub" library is needed here because Converter_ut.cpp
    needs to include "Converter.hpp" from src directory

Thank you in advance. I have read plenty of sites / tutorials already but I am still not sure about that.

One more thing – I cannot really imagine project with plenty of source files. Isn't there a better way to add source files to add_library and add_executable functions than listing it manually? Something like "take all *.cpp files from current directory"?

Thanks.

Best Solution

Cmake is not properly a programming language supporting a full paradigm, so use it, but if possible never start creating "a framework with it" (it would be cumbersome without proper syntactic sugar), it is intended to make small scripts not to write thousand lines of code (and despite few good frameworks exists, I tend to not use them: "If I cannot code it in few lines, then it's not job for CMAKE").

The important parts are (not that it is slightly different, I copy-pasted the improved version i still have to commit):

cmake_minimum_required( VERSION 2.8)
project( Infectorpp2)

# find additional cmake scripts (I'm driving away from this)
set( CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")

# create list of files from a glob
file( GLOB INFECTOR_SOURCE_FILES
                        "include/Infectorpp/priv/*.cpp")

# create list of files from a glob
file( GLOB INFECTOR_HEADER_FILES
                        "include/Infectorpp/priv/*.hpp"
                        "include/Infectorpp/*.hpp")

# or just "add_executable" the dollar "${}" just expand the list
add_library( libInfectorpp2 STATIC
                        ${INFECTOR_SOURCE_FILES}
                        ${INFECTOR_HEADER_FILES})

If you are not using 3rd party libraries, then you do not need to add target_include_directories because for your own application relative paths suffice.

For the testing part you are mostly ok to me, but I would do:

enable_testing()

## details omitted...

 # create list of files from a glob
file( GLOB GMOCK_TESTS_SOURCE_FILES
                        "locationToTestFiles/*.cpp")

# Executable
add_executable (tests
    ${GMOCK_TESTS_SOURCE_FILES})
target_link_libraries (tests gmock_main Sub)

add_test(tests tests)

Also note that CMAKE is the only reason why I find useful having different extensions for C++ files, because of GLOBS, if you want to exclude some file you have to change its extension (to cc, c++, cxx or what your prefer).

You can do mostly anything following the same pattern, note that with GLOB you have to re-configure CMake to detect newly added files, however still better than adding them manually to build script (and anyway that will not cause a whole recompilation, CMake keep track of data and will avoid to re-compile old files)

Some people find useful adding files manually to CMake scripts because "I can keep old file there". Don't do that, move old files into an "old" folder, or just let your subversion system keep memory of them for you. You will catch most errors earlier, and you will have a "ready to ship" project (you won't accidentally left wrong files that users will attempt to compile)

Another important point: Do out of source builds, from your script I guess you are still not doing that.

Related Question