CMake - зависимости (заголовки) между приложениями / библиотеками в одном проекте
У меня есть следующая структура проекта:
- CMakeLists.txt
- lib1/CMakeLists.txt и все cpp и заголовочные файлы библиотеки
- lib2/CMakeLists.txt и все cpp и заголовочные файлы библиотеки
- app/CMakeLists.txt и все cpp и заголовочные файлы приложения
Основной CMakeLists.txt выглядит так:
PROJECT( ${PROJECT_NAME} )
add_subdirectory(lib1)
add_subdirectory(lib2)
add_subdirectory(app)
Lib1/CMakeLists.txt выглядит, например, как (раздетый):
SET(SOURCE
file.cpp
)
SET(HEADERS
some_lib_header.h
)
add_library( lib1 ${SOURCE} ${HEADERS} )
и тот, который для приложения выглядит так же, за исключением ADD_EXECUTABLE:
SET(SOURCE
main.cpp
)
SET(HEADERS
some_header.h
)
add_library( lib1 ${SOURCE} ${HEADERS} )
ADD_EXECUTABLE( app ${SOURCE} ${HEADERS} )
Я обнаружил, что установка хорошо работает таким образом, потому что из этого я могу сгенерировать один файл решения Visual Studio, который содержит все эти три проекта. Но моя проблема в том, что мое приложение содержит заголовочные файлы lib1 (а также lib2, которая зависит от lib1). Когда я делаю
$mkdir build
$cd build
$cmake -C ..\myproject
он генерирует VS .sln-файл из исходных кодов, как я хочу, но приложение не компилируется, потому что не может найти заголовочные файлы lib1 (очевидно).
Сейчас я прочитал и перепробовал много вещей, вроде TARGET_LINK_LIBRARIES( app lib1 )
(которое заставило приложение связываться с lib1, но не решило проблему включения заголовка), и тому подобное add_subdirectory( ../lib1 )
в различных вариантах в CMakeLists.txt приложения (которое выдает все ошибки, которые я не мог исправить), а также find_package (который, я думаю, является неправильным подходом).
Так как я могу решить эту (я думаю, простую...) проблему?
2 ответа
Вот одно из возможных решений:
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)
Приложение /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)
Есть много разных способов достижения одного и того же конечного результата. Для относительно небольшого проекта я бы, вероятно, просто использовал один 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)
Для получения дополнительной информации о соответствующих командах и их обосновании выполните:
cmake --help-command add_subdirectory
cmake --help-command include_directories
cmake --help-command target_link_libraries
Project
CMakeLists.txt
\-lib1
CMakeLists.txt
\- include \ lib1
\- src
\-lib2
CMakeLists.txt
\- include \ lib2
\- src
\-app
CMakeLists.txt
\- src
Предположим зависимости следующим образом:
lib1 ---> lib2 ---> app
\--------------> app
Что-то вроде этого:
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)
так что в lib2/src/file.cpp вы можете сделать #include <lib1/header.hpp>
Приложение /CMakeLists.txt:
file(GLOB_RECURSE _SRCS "src/*.[hc]pp")
add_executable(app ${_SRCS})
target_link_libraries(app lib1 lib2)
install(TARGETS app DESTINATION bin)
так что в app/src/file.cpp вы можете сделать #include <lib1/header.hpp>
а также #include <lib2/header.hpp>
Волшебство - target_include_directories, которые присоединяют каталог "include" к цели, поэтому, связываясь с ним, вы также тянете каталог include;)