How to add "out-of-tree" common source files

I have a project with the following directory structure

server
  | daemon
  |  | src
  | service
  |  | src
  | commons

The daemon is the linux version and service contains the windows version. Originally, I made 2 separate cmake projects in those 2 folders and it worked. But I had to copy paste code between both projects. Now I want to move sources with common code into the commons folder. So that each project includes the sources from the common folder instead of copy pasting code.

Within Code::Blocks, it works, I just add the commons files to the project and it works.

For CMake, it works only on header files using a command like:

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../commons)

in the scr directory. Now the problem is when I want to include source files in the same directory. I cannot manage it to make it work, it does not find the source files. Adding a new sub directory does not work since it’s not a sub directory.

add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../commons commons)

I don’t seem to be able to build the commons folder without using add_executable or add_library. Making a library could work, but I though generating a library would be an overkill since it will not be reused elsewhere than this project.

Then I though that maybe I could create the project in the root directory and add 2 projects for the whole tree structure. But adding 2 projects in the same file seems impossible. I also have the constraint that I don’t want to build every thing all the time. When I am in linux, I want to build daemon and commons while when I am in windows, I want to build service and commons.

The internet did not yield any good solution so far. So I am trying this forum:

Is it possible to add “out-of-tree” source files?
Else is it possible to have multiple projects in the same cmake file and only build a portion of the projects?
Else is there other solutions than building a static library?

This might be where an OBJECT library can help. It’s like a static library except the objects go directly on the consumer’s link line rather than through an actual static library. Since it seems you’re building executables, the oddities one has to deal with for exporting symbols from object libraries isn’t relevant here.

Ok, it works. Here is what I did for those who stumble on this thread. First I defined a config file in my commons directory. I added a library as type OBJECT with the matching C files and I want to include headers in the same folder. My “library” name is going to be cvpcommons

include_directories(.)
add_library(cvpcommons OBJECT tcpconnection.c commons.c)

Then I added the “sub-directory” in the project root config file by specifying an alternate location to store the built files. In my case it was src/commons.

cmake_minimum_required(VERSION 3.7)
project(cvpd)
add_subdirectory(src)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../commons src/commons)

Finally, the src directory config must add the common folder for includes and linking directories. Then you must add the library to the executable using the $<TARGET_OBJECTS:...> directive.

include_directories(.)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../commons)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../commons)
add_executable(cvpd main.c keypress.c $<TARGET_OBJECTS:cvpcommons>)
target_link_libraries(cvpd X11 Xtst )
install(TARGETS cvpd RUNTIME DESTINATION ${PROJECT_SOURCE_DIR})

This is how I managed to make it work.

You could just add a single target at the server level, then add sources from common and one of daemon or service depending on platform. Based on what you’ve described you want to do, this is how I would structure it:

server/CMakeLists.txt:

add_executable(cvpcommons)

add_subdirectory(commons)
if(WIN32)
    add_subdirectory(service)
else()
    add_subdirectory(daemon)
endif()

service/CMakeLists.txt:

target_sources(cvpcommons file1.cpp file2.cpp ...)

daemon/CMakeLists.txt:

target_sources(cvpcommons file3.cpp file4.cpp ...)

commons/CMakeLists.txt:

target_sources(cvpcommons file5.cpp file6.cpp ...)

# Make this directory part of the header search path so the others find it
target_include_directories(cvpcommons PRIVATE .)

Some further reading: Enhanced source file handling with target_sources() - Crascit

2 Likes