28

I have the following project structure:

  • CMakeLists.txt
  • lib1/CMakeLists.txt and all cpp and header files of the lib
  • lib2/CMakeLists.txt and all cpp and header files of the lib
  • app/CMakeLists.txt and all cpp and header files of the app

The main CMakeLists.txt looks like:

PROJECT( ${PROJECT_NAME} )
add_subdirectory(lib1)
add_subdirectory(lib2)
add_subdirectory(app)

The lib1/CMakeLists.txt looks eg like (stripped):

SET(SOURCE
    file.cpp
)
SET(HEADERS
    some_lib_header.h
)
add_library( lib1 ${SOURCE} ${HEADERS} )

and the one for the app looks the same except of ADD_EXECUTABLE:

SET(SOURCE
    main.cpp
)
SET(HEADERS
    some_header.h
)
add_library( lib1 ${SOURCE} ${HEADERS} )
ADD_EXECUTABLE( app ${SOURCE} ${HEADERS} )

I found the setup working well this way because out of this, I can generate one Visual Studio solution file which contains all those three projects. But my problem is that my app includes header files of lib1 (and also of lib2, which depends on lib1). When I do

$mkdir build
$cd build
$cmake -C ..\myproject

it generates out-of-source VS .sln file as I want it, but the app doesn't compile because it can't find the header files of lib1 (obviously).

Now I read and tried many things, like TARGET_LINK_LIBRARIES( app lib1 ) (which got the app to link with the lib1, but not solve the header include issue), and things like add_subdirectory( ../lib1 ) in various variants in the CMakeLists.txt of app (which all throwed errors that I couldn't fix), and also find_package (which I guess is the wrong approach).

So how can I solve this (I guess simple...) problem?

ilya1725
  • 4,496
  • 7
  • 43
  • 68
Ela782
  • 5,041
  • 5
  • 53
  • 66

2 Answers2

29

Here's one possible solution:

Root CMakeLists.txt:

cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project(${PROJECT_NAME})
add_subdirectory(lib1)
add_subdirectory(lib2)
add_subdirectory(app)


lib1/CMakeLists.txt:

project(Lib1)
add_library(lib1 lib1.cpp lib1.h)


lib2/CMakeLists.txt:

project(Lib2)
add_library(lib2 lib2.cpp lib2.h)

# Add /lib1 to #include search path
include_directories(${Lib1_SOURCE_DIR})
# Specify lib2's dependency on lib1
target_link_libraries(lib2 lib1)


app/CMakeLists.txt:

project(App)
add_executable(app main.cpp some_header.h)

# Add /lib1 and /lib2 to #include search path
include_directories(${Lib1_SOURCE_DIR} ${Lib2_SOURCE_DIR})
# Specify app's dependency on lib2.
# lib2's dependency on lib1 is automatically added.
target_link_libraries(app lib2)


There are plenty of different ways to achieve the same end result here. For a relatively small project, I'd probably just use a single CMakeLists.txt:

cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project(Test)

add_library(lib1 lib1/lib1.cpp lib1/lib1.h)
add_library(lib2 lib2/lib2.cpp lib2/lib2.h)
add_executable(app app/main.cpp app/some_header.h)

include_directories(${CMAKE_SOURCE_DIR}/lib1 ${CMAKE_SOURCE_DIR}/lib2)

target_link_libraries(lib2 lib1)
target_link_libraries(app lib2)


For further info on the relevant commands and their rationale, run:

cmake --help-command add_subdirectory
cmake --help-command include_directories
cmake --help-command target_link_libraries
Fraser
  • 74,704
  • 20
  • 238
  • 215
  • 1
    The project is pretty big, so I don't like one single CMakeLists.txt. – Ela782 Jun 26 '12 at 22:53
  • Your first proposed solution has one problem: You specify a project(...) at the beginning of each lib/app CMakeLists.txt file. Isn't that bad style and I should use project(...) only once in the root CMakeLists.txt? Also, if I use project(...) in each CMakeLists.txt file, CMake generates a .sln file for each project (in addition to the root .sln with all projects in it) and other unneccesary stuff, which is kind of overhead that is never used and also probably not the best solution? – Ela782 Jun 26 '12 at 22:55
  • @Ela782 Yeah - no worries. Multiple CMakeLists.txt files can simplify things pretty well for anything but the smallest of projects. – Fraser Jun 26 '12 at 22:55
  • @Ela782 No - there will be only a single top-level .sln The docs for [`add_subdirectory`](http://www.cmake.org/cmake/help/v2.8.8/cmake.html#command:add_subdirectory) explain this pretty well. – Fraser Jun 26 '12 at 22:57
  • As soon as I add a e.g. "project(App)" to my apps CMakeLists.txt, I also get a second sln file inside app-dir, along with some other files. It's not the add_subdirectory that's causing this. – Ela782 Jun 26 '12 at 23:01
  • Hmm I just read your link to the help section, and now I'm even more confused how to do this all properly... – Ela782 Jun 26 '12 at 23:04
  • If you invoke cmake directly on app/CMakeLists.txt, then yes you would get App.sln. However, if you invoke cmake only on the root CMakeLists.txt, you should end up with just a single .sln file and multiple .vcxproj (or similar) project files. – Fraser Jun 26 '12 at 23:07
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/13085/discussion-between-fraser-and-ela782) – Fraser Jun 26 '12 at 23:12
  • OK - my stupid mistake :-) I was running a test with the monolithic CMakeLists.txt thinking I was looking at the output for the distributed version in the first option! The first option does indeed create multiple .sln files. My bad. Sorry for the confusion. – Fraser Jun 27 '12 at 00:02
  • @Fraser, Ela782: I recommend using a top-level CMakeLists.txt with add_subdirectory commands and multiple "child" CMakeLists.txt with a project-command. That way you could also open and build lib2.sln which will automatically build dependent lib1, but not app – André Jun 27 '12 at 13:18
  • @Andre yup - for all but the smallest of projects I agree. – Fraser Jun 27 '12 at 13:30
  • Thank you very much, this is exactly how I do it now. This is very reassuring! Thank you! – Ela782 Jun 27 '12 at 23:05
10
Project
 CMakeLists.txt
 \-lib1
   CMakeLists.txt
   \- include \ lib1
   \- src
 \-lib2
   CMakeLists.txt
   \- include \ lib2
   \- src
 \-app
   CMakeLists.txt
   \- src

Suppose dependencies as follow:

lib1 ---> lib2 ---> app 
   \--------------> app

Something like this:

CMakeLists.txt:

add_subdirectory(lib1)
add_subdirectory(lib2)
add_subdirectory(app)

lib1/CMakeLists.txt:

  file(GLOB_RECURSE _HDRS "include/*.hpp")
  file(GLOB_RECURSE _SRCS "src/*.[hc]pp")
  add_library(lib1 ${_HDRS} ${_SRCS})
  #target_link_libraries(lib1)
  target_include_directories(lib1 PUBLIC include)

  install(TARGETS lib1 DESTINATION lib)
  install(FILES ${_HDRS} DESTINATION include/lib1)

lib2/CMakeLists.txt:

  file(GLOB_RECURSE _HDRS "include/*.hpp")
  file(GLOB_RECURSE _SRCS "src/*.[hc]pp")
  add_library(lib2 ${_HDRS} ${_SRCS})
  target_link_libraries(lib2 lib1)
  target_include_directories(lib2 PUBLIC include)

  install(TARGETS lib2 DESTINATION lib)
  install(FILES ${_HDRS} DESTINATION include/lib2)

so in lib2/src/file.cpp you could do #include <lib1/header.hpp>

app/CMakeLists.txt:

  file(GLOB_RECURSE _SRCS "src/*.[hc]pp")
  add_executable(app ${_SRCS})
  target_link_libraries(app lib1 lib2)

  install(TARGETS app DESTINATION bin)

so in app/src/file.cpp you could do #include <lib1/header.hpp> and #include <lib2/header.hpp>

The magic is target_include_directories which attach the "include" directory to the target, so when linking with it you pull the include directory also ;)

Mizux
  • 8,222
  • 7
  • 32
  • 48
  • 1
    only sad is cmake don't install headers of a target so you need an ugly extra `install(files ...)` I've tries `PUBLIC_HEADER DESTINATION include/lib` without success inside the `install(TARGETS ...)` – Mizux Feb 10 '16 at 14:33
  • This is a more appropriate way to achieve this, because here the one doesn't have to list dependency headers manually – Vitaly Isaev Sep 16 '18 at 16:24