2

I just got into cmake because I started working on a bigger project. I need to add module tests. There are several embedded devices, each has it own application running on it.

Most of these applications share code. So the code is divided into modules. The problem is that some modules are used in most of the other modules. These modules are common, common-net and log.

A simplified project (app) for a device has following structure:

    .
    |-- CMakeLists.txt (for app)
    |-- LICENSE
    |-- app
    |   |-- inc
    |   |   |-- appfile1.hpp
    |   |   `-- appfile2.hpp
    |   `-- src
    |       |-- appfile1.cpp
    |       |-- appfile2.cpp
    |       `-- main.cpp
    |-- comp
    |   |-- comp1
    |   |   |-- CMakeLists.txt (for test)
    |   |   |-- comp
    |   |   |   |-- CMakeLists.txt (for lib)
    |   |   |   |-- intf
    |   |   |   |   |-- comp1file.hpp
    |   |   |   |   `-- comp1file.hpp
    |   |   |   `-- src
    |   |   |       |-- comp1file.cpp
    |   |   |       `-- comp1file.cpp
    |   |   `-- test
    |   |       `-- src
    |   |           `-- comp1testfile.cpp
    |   |-- comp2
    |   |   |-- CMakeLists.txt (for test)
    |   |   |-- comp
    |   |   |   |-- CMakeLists.txt (for lib)
    |   |   |   |-- inc
    |   |   |   |   `-- comp2file1.hpp
    |   |   |   |-- intf
    |   |   |   |   |-- comp2file2.hpp
    |   |   |   |   |-- comp2file3.hpp
    |   |   |   |   |-- comp2file4.hpp
    |   |   |   `-- src
    |   |   |       |-- comp2file1.cpp
    |   |   |       |-- comp2file2.cpp
    |   |   |       `-- comp2file3.cpp
    |   |   `-- test
    |   |       |-- inc
    |   |       |   `-- comp2testfile.hpp
    |   |       `-- src
    |   |           `-- comp2testfile.cpp
    |   |-- common
    |   |   |-- CMakeLists.txt (for test)
    |   |   `-- comp
    |   |       |-- inc
    |   |       |   |-- commonfile1.hpp
    |   |       |   `-- commonfile2.hpp
    |   |       `-- src
    |   |           `-- commonfile1.cpp
    |   |-- common-net
    |   |   |-- CMakeLists.txt (for test)
    |   |   |-- comp
    |   |   |   |-- inc
    |   |   |   |   |-- netfile1.hpp
    |   |   |   |   |-- netfile2.hpp
    |   |   |   |   |-- netfile3.hpp
    |   |   |   |   |-- netfile4.hpp
    |   |   |   |   |-- netfile5.hpp
    |   |   |   `-- src
    |   |   |       |-- netfile1.cpp
    |   |   |       |-- netfile2.cpp
    |   |   |       |-- netfile3.cpp
    |   |   |       |-- netfile4.cpp
    |   |   |       |-- netfile5.cpp
    |   |   `-- test
    |   |       |-- inc
    |   |       |   |-- nettestfile1.hpp
    |   |       `-- src
    |   |           |-- nettestfile1.cpp
    |   |           |-- nettestfile2.cpp
    |   |           |-- nettestfile3.cpp
    |   |-- comp3
    |   |   |-- CMakeLists.txt (for test)
    |   |   |-- comp
    |   |   |   |-- CMakeLists.txt (for lib)
    |   |   |   |-- intf
    |   |   |   |   |-- comp3file1.hpp
    |   |   |   |   `-- comp3file2.hpp
    |   |   |   `-- src
    |   |   |       `-- comp3file1.cpp
    |   |   `-- test
    |   |       `-- src
    |   |           `-- comp3testfile1.cpp
    |   |-- log
    |   |   `-- comp
    |   |       |-- inc
    |   |       |   |-- logfile1.hpp
    |   |       |   |-- logfile2.hpp
    |   |       |-- intf
    |   |       |   |-- logfile3.hpp
    |   |       |   |-- logfile4.hpp
    |   |       `-- src
    |   |           |-- logfile1.cpp
    |   |           |-- logfile2.cpp
    |   |           |-- logfile3.cpp
    |   |           `-- logfile4.cpp
    |   |-- comp4
    |   |   |-- CMakeLists.txt (for test)
    |   |   |-- comp
    |   |   |   |-- CMakeLists.txt (for lib)
    |   |   |   |-- intf
    |   |   |   |   |-- comp4file1.hpp
    |   |   |   |   |-- comp4file2.hpp
    |   |   |   |   |-- comp4file3.hpp
    |   |   |   `-- src
    |   |   |       |-- comp4file1.cpp
    |   |   |       |-- comp4file2.cpp
    |   |   |       `-- comp4file3.cpp
    |   |   `-- test
    |   |       |-- inc
    |   |       |   |-- comp4testfile1.hpp
    |   |       |   `-- comp4testfile2.hpp
    |   |       `-- src
    |   |           |-- comp4testfile1.cpp
    |   |           |-- comp4testfile2.cpp
    |   |           |-- comp4testfile3.cpp
    |   |           |-- comp4testfile4.cpp
    |   |           `-- comp4testfile5.cpp
    |-- gcc-4.8.cmake
    |-- gcc-4.9.cmake
    `-- gcc-default.cmake

I know it has a lot of "comp" in it, but that naming was not up to me..

Each comp has 2 CMakefiles: The top level one is to create a unit test executable to test the module. The one in comp is to make a library. So that it can be used with add_subdirectory in the app CMakefile.

The problem is that a lot of modules are dependent of common, common-net and log. Currently those parts are not build as libs. I'm guessing cause add_subdirectory gave problems if used in multiple modules? Is this clean?

This leads to: Each dependent module includes the common headers in the comp CMakefile. At the top level (app CMakefile) the common sources are added to the executable. But for each module test I also have to add the common sources to the test executable.

This seems strange to me and I think something is wrong. But I dont know how to solve it cleanly?

Looking at the project tree and the need to test each module independently. Is the approach to add sources at the top level ok or is that not done?

rinn2883
  • 366
  • 1
  • 14

1 Answers1

8

So, if I understand properly, you have two separate problems: having to add the common sources to each executable, and having to include the common headers for each executable.

What you really should do is make each of the common code libraries a CMake static library target. CMake resolves dependencies between libraries and executables within a project, which is what you have. As long as you add the subdirectories with the common code (common, common-net, and log) before the things that use that code you can just do something like

target_link_libraries(comp2 common common-net log)

in each of the comp folders, and CMake will handle the dependencies and the linking.

What's more, you can attach include paths (among other things) to a target, and they will automatically get applied to anything that links to it.

So, for the log library for example, you'd use

target_include_directories(log PUBLIC inc intf)

Then, anything which linked to the log library would automatically add the comp/log/comp/inc and comp/log/comp/intf folders to its header path.

Finally, if I understand correctly, you are add_subdirectory-ing the comp4/CMakeLists.txt files when you need an executable test, and using the comp4/comp/CMakeLists.txt files when you need to build the library for including into the app. A better way to do this is to have the tests also link to the libraries.

So, in conclusion, your project structure should look a bit like this:

Top-level CMakeLists.txt

add_subdirectory(comp)
add_subdirectory(app)

comp/CMakeLists.txt

add_subdirectory(common)
add_subdirectory(common-net)
add_subdirectory(log)
add_subdirectory(comp1)
<and so on for the other comps>

comp/common-net/CMakeLists.txt

add_subdirectory(comp)
add_executable(test-common-net <source for test>)
target_link_libraries(test-common-net common-net)

comp/common-net/comp/CMakeLists.txt

add_library(common-net STATIC <source for common-net>)
target_include_directories(common-net PUBLIC inc)

# assuming common-net depends on common
target_link_libraries(common-net common)

comp/comp1/CMakeLists.txt

add_subdirectory(comp)
add_executable(test-comp1 <source for test>)
target_link_libraries(test-comp1 comp1)

comp/comp1/comp/CMakeLists.txt

add_library(comp1 STATIC <source for comp1>)
target_include_directories(comp1 PUBLIC inc)

target_link_libraries(comp1 common common-net log)

You probably get the idea by now. You just need to create a chain of libraries that depend on each other.

  • ty i remade some stuff with your pointers. I think its a lot cleaner. Hopefully the team will see that this way is better. – rinn2883 Mar 23 '17 at 09:35