如何通过CMake配置实现多库同名头文件的命名空间引入
解决同名头文件的CMake配置方案
我有两个(或更多)包含同名头文件(比如utils.h)的库,在项目里需要不同场景下使用这些头文件里的类,但常规引入方式因为文件名歧义无法正确引入目标文件。很多库通过给头文件添加命名空间路径(比如#include <rapidjson/document.h>)解决这个问题,我想实现这个方案,请问怎么用CMake配置?
以下是我当前的相关CMake配置代码:
主项目GISManager的CMakeLists.txt
cmake_minimum_required(VERSION 3.5) project(GISManager LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(OSMManager REQUIRED) #First libray find_package(GeoJSONParser REQUIRED) #Second library set(GISManager_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src) set(GISManager_H ${CMAKE_CURRENT_SOURCE_DIR}/include) set(Hdrs ${GISManager_H}/gismanager.h ) set(Srcs ${GISManager_SRC}/gismanager.cpp) add_library(GISManager ${Hdrs} ${Srcs}) target_include_directories(GISManager PUBLIC $<BUILD_INTERFACE:${GISManager_H}/> ${OSMManager_INCLUDE_DIRS}/ ${GeoJSONParser_INCLUDE_DIRS}/ ) target_link_libraries(GISManager ${OSMManager_LIBRARIES} ${GeoJSONParser_LIBRARIES}) add_executable(GISManager_Test ${Hdrs} ${Srcs} ${GISManager_SRC}/main.cpp) target_include_directories(GISManager_Test PUBLIC $<BUILD_INTERFACE:${GISManager_H}/> ${OSMManager_INCLUDE_DIRS}/ ${GeoJSONParser_INCLUDE_DIRS}/ ) target_link_libraries(GISManager_Test ${OSMManager_LIBRARIES} ${GeoJSONParser_LIBRARIES}) install(FILES ${Hdrs} DESTINATION include/GISManager-${version}) install(TARGETS GISManager DESTINATION lib/GISManager-${version} EXPORT GISManager-targets) install(EXPORT GISManager-targets DESTINATION lib/GISManager-${version}) configure_file( ${GISManager_SOURCE_DIR}/pkg/gismanager-config.cmake.in ${GISManager_BINARY_DIR}/pkg/gismanager-config.cmake @ONLY) configure_file( ${GISManager_SOURCE_DIR}/pkg/gismanager-config-version.cmake.in ${GISManager_BINARY_DIR}/pkg/gismanager-config-version.cmake @ONLY) install(FILES ${GISManager_BINARY_DIR}/pkg/gismanager-config.cmake ${GISManager_BINARY_DIR}/pkg/gismanager-config-version.cmake DESTINATION lib/GISManager-${version})
GeoJSONParser库的CMakeLists.txt
cmake_minimum_required(VERSION 3.5) project(GeoJSONParser LANGUAGES CXX) set(version 1.0) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(GeoJSONParser_H ${CMAKE_CURRENT_SOURCE_DIR}/include) set(GeoJSONParser_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src) find_path(RapidJSON_INCLUDE_DIRS rapidjson) set(HDRS ${GeoJSONParser_H}/object.h ${GeoJSONParser_H}/featurecollection.h ${GeoJSONParser_H}/feature.h ${GeoJSONParser_H}/geometry.h ${GeoJSONParser_H}/point.h ${GeoJSONParser_H}/multipoint.h ${GeoJSONParser_H}/linestring.h ${GeoJSONParser_H}/multilinestring.h ${GeoJSONParser_H}/polygon.h ${GeoJSONParser_H}/multipolygon.h ${GeoJSONParser_H}/geometrycollection.h ${GeoJSONParser_H}/utils.h ${GeoJSONParser_H}/types.h ) set(SRCS ${GeoJSONParser_SRC}/featurecollection.cpp ${GeoJSONParser_SRC}/feature.cpp ${GeoJSONParser_SRC}/point.cpp ${GeoJSONParser_SRC}/multipoint.cpp ${GeoJSONParser_SRC}/linestring.cpp ${GeoJSONParser_SRC}/multilinestring.cpp ${GeoJSONParser_SRC}/polygon.cpp ${GeoJSONParser_SRC}/multipolygon.cpp ${GeoJSONParser_SRC}/geometrycollection.cpp ${GeoJSONParser_SRC}/utils.cpp ) add_library(GeoJSONParser ${HDRS} ${SRCS}) target_include_directories(GeoJSONParser PUBLIC $<BUILD_INTERFACE:${GeoJSONParser_H}/> ${RapidJSON_INCLUDE_DIRS}) add_executable(GeoJSONParser_Test ${HDRS} ${SRCS} ${GeoJSONParser_SRC}/main.cpp) target_include_directories(GeoJSONParser_Test PUBLIC $<BUILD_INTERFACE:${GeoJSONParser_H}/> ${RapidJSON_INCLUDE_DIRS}) install(FILES ${HDRS} DESTINATION include/GeoJSONParser-${version}) install(TARGETS GeoJSONParser DESTINATION lib/GeoJSONParser-${version} EXPORT GeoJSONParser-targets) install(EXPORT GeoJSONParser-targets DESTINATION lib/GeoJSONParser-${version}) configure_file( ${GeoJSONParser_SOURCE_DIR}/pkg/geojsonparser-config.cmake.in ${GeoJSONParser_BINARY_DIR}/pkg/geojsonparser-config.cmake @ONLY) configure_file( ${GeoJSONParser_SOURCE_DIR}/pkg/geojsonparser-config-version.cmake.in ${GeoJSONParser_BINARY_DIR}/pkg/geojsonparser-config-version.cmake @ONLY) install(FILES ${GeoJSONParser_BINARY_DIR}/pkg/geojsonparser-config.cmake ${GeoJSONParser_BINARY_DIR}/pkg/geojsonparser-config-version.cmake DESTINATION lib/GeoJSONParser-${version})
OSMManager库的CMakeLists.txt
cmake_minimum_required(VERSION 3.5) project(OSMManager LANGUAGES CXX) set(version 1.0) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(OSMManager_H ${CMAKE_CURRENT_SOURCE_DIR}/include) set(OSMManager_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src) find_path(RapidJSON_INCLUDE_DIRS rapidjson) find_package(PROJ) set(HDRS ${OSMManager_H}/root.h ${OSMManager_H}/node.h ${OSMManager_H}/way.h ${OSMManager_H}/relation.h ${OSMManager_H}/coordinatesconverter.h ${OSMManager_H}/utils.h ${OSMManager_H}/types.h ${OSMManager_H}/tinyxml2.h ) set(SRCS ${OSMManager_SRC}/root.cpp ${OSMManager_SRC}/node.cpp ${OSMManager_SRC}/way.cpp ${OSMManager_SRC}/relation.cpp ${OSMManager_SRC}/coordinatesconverter.cpp ${OSMManager_SRC}/tinyxml2.cpp ) add_library(OSMManager ${HDRS} ${SRCS}) target_include_directories(OSMManager PUBLIC $<BUILD_INTERFACE:${OSMManager_H}/> ${RapidJSON_INCLUDE_DIRS} ${PROJ_INCLUDE_DIRS}) target_link_libraries(OSMManager ${PROJ_LIBRARIES}) add_executable(OSMManager_Test ${HDRS} ${SRCS} ${OSMManager_SRC}/main.cpp) target_include_directories(OSMManager_Test PUBLIC $<BUILD_INTERFACE:${OSMManager_H}/> ${RapidJSON_INCLUDE_DIRS} ${PROJ_INCLUDE_DIRS}) target_link_libraries(OSMManager_Test ${PROJ_LIBRARIES}) install(FILES ${HDRS} DESTINATION include/OSMManager-${version}) install(TARGETS OSMManager DESTINATION lib/OSMManager-${version} EXPORT OSMManager-targets) install(EXPORT OSMManager-targets DESTINATION lib/OSMManager-${version}) configure_file( ${OSMManager_SOURCE_DIR}/pkg/osmmanager-config.cmake.in ${OSMManager_BINARY_DIR}/pkg/osmmanager-config.cmake @ONLY) configure_file( ${OSMManager_SOURCE_DIR}/pkg/osmmanager-config-version.cmake.in ${OSMManager_BINARY_DIR}/pkg/osmmanager-config-version.cmake @ONLY) install(FILES ${OSMManager_BINARY_DIR}/pkg/osmmanager-config.cmake ${OSMManager_BINARY_DIR}/pkg/osmmanager-config-version.cmake DESTINATION lib/OSMManager-${version})
GeoJSONParser的config.cmake.in
# Compute installation prefix relative to this file. get_filename_component(_dir "${CMAKE_CURRENT_LIST_FILE}" PATH) get_filename_component(_prefix "${_dir}/../.." ABSOLUTE) # Import the targets. include("${_prefix}/lib/GeoJSONParser-@version@/GeoJSONParser-targets.cmake") # Report other information. set(GeoJSONParser_INCLUDE_DIRS "${_prefix}/include/GeoJSONParser-@version@/") file(GLOB GeoJSONParser_LIBRARIES "${_prefix}/lib/GeoJSONParser-@version@/lib*")
解决方案
1. 调整库的目录结构
把每个库的头文件放到与库名同名的子目录下,形成如下结构:
# GeoJSONParser目录结构 GeoJSONParser/ ├── include/ │ └── GeoJSONParser/ # 新增子目录,和库名一致 │ ├── object.h │ ├── featurecollection.h │ ├── ... │ └── utils.h └── src/ # OSMManager目录结构 OSMManager/ ├── include/ │ └── OSMManager/ # 新增子目录,和库名一致 │ ├── root.h │ ├── node.h │ ├── ... │ └── utils.h └── src/
2. 修改库的CMakeLists.txt配置
以GeoJSONParser为例,修改后的配置如下:
cmake_minimum_required(VERSION 3.5) project(GeoJSONParser LANGUAGES CXX) set(version 1.0) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 头文件根目录指向include,而非include/GeoJSONParser set(GeoJSONParser_H ${CMAKE_CURRENT_SOURCE_DIR}/include) set(GeoJSONParser_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src) find_path(RapidJSON_INCLUDE_DIRS rapidjson) # 更新头文件路径到子目录下 set(HDRS ${GeoJSONParser_H}/GeoJSONParser/object.h ${GeoJSONParser_H}/GeoJSONParser/featurecollection.h ${GeoJSONParser_H}/GeoJSONParser/feature.h ${GeoJSONParser_H}/GeoJSONParser/geometry.h ${GeoJSONParser_H}/GeoJSONParser/point.h ${GeoJSONParser_H}/GeoJSONParser/multipoint.h ${GeoJSONParser_H}/GeoJSONParser/linestring.h ${GeoJSONParser_H}/GeoJSONParser/multilinestring.h ${GeoJSONParser_H}/GeoJSONParser/polygon.h ${GeoJSONParser_H}/GeoJSONParser/multipolygon.h ${GeoJSONParser_H}/GeoJSONParser/geometrycollection.h ${GeoJSONParser_H}/GeoJSONParser/utils.h ${GeoJSONParser_H}/GeoJSONParser/types.h ) set(SRCS ${GeoJSONParser_SRC}/featurecollection.cpp ${GeoJSONParser_SRC}/feature.cpp ${GeoJSONParser_SRC}/point.cpp ${GeoJSONParser_SRC}/multipoint.cpp ${GeoJSONParser_SRC}/linestring.cpp ${GeoJSONParser_SRC}/multilinestring.cpp ${GeoJSONParser_SRC}/polygon.cpp ${GeoJSONParser_SRC}/multipolygon.cpp ${GeoJSONParser_SRC}/geometrycollection.cpp ${GeoJSONParser_SRC}/utils.cpp ) add_library(GeoJSONParser ${HDRS} ${SRCS}) # 设置头文件搜索根目录,编译时指向本地include,安装后指向系统include target_include_directories(GeoJSONParser PUBLIC $<BUILD_INTERFACE:${GeoJSONParser_H}> $<INSTALL_INTERFACE:include> ${RapidJSON_INCLUDE_DIRS} ) add_executable(GeoJSONParser_Test ${HDRS} ${SRCS} ${GeoJSONParser_SRC}/main.cpp) target_include_directories(GeoJSONParser_Test PUBLIC $<BUILD_INTERFACE:${GeoJSONParser_H}> ${RapidJSON_INCLUDE_DIRS} ) # 安装时保留子目录结构,直接安装整个GeoJSONParser目录到include下 install(DIRECTORY ${GeoJSONParser_H}/GeoJSONParser DESTINATION include) install(TARGETS GeoJSONParser DESTINATION lib/GeoJSONParser-${version} EXPORT GeoJSONParser-targets) install(EXPORT GeoJSONParser-targets DESTINATION lib/GeoJSONParser-${version}) configure_file( ${GeoJSONParser_SOURCE_DIR}/pkg/geojsonparser-config.cmake.in ${GeoJSONParser_BINARY_DIR}/pkg/geojsonparser-config.cmake @ONLY) configure_file( ${GeoJSONParser_SOURCE_DIR}/pkg/geojsonparser-config-version.cmake.in ${GeoJSONParser_BINARY_DIR}/pkg/geojsonparser-config-version.cmake @ONLY) install(FILES ${GeoJSONParser_BINARY_DIR}/pkg/geojsonparser-config.cmake ${GeoJSONParser_BINARY_DIR}/pkg/geojsonparser-config-version.cmake DESTINATION lib/GeoJSONParser-${version})
OSMManager的CMakeLists.txt做同样修改:
- 更新头文件路径到
include/OSMManager/xxx.h - 调整
target_include_directories添加$<INSTALL_INTERFACE:include> - 用
install(DIRECTORY ${OSMManager_H}/OSMManager DESTINATION include)代替原有的install(FILES)
3. 修改库的config.cmake.in文件
以GeoJSONParser为例,修改后的配置如下:
# Compute installation prefix relative to this file. get_filename_component(_dir "${CMAKE_CURRENT_LIST_FILE}" PATH) get_filename_component(_prefix "${_dir}/../.." ABSOLUTE) # Import the targets. include("${_prefix}/lib/GeoJSONParser-@version@/GeoJSONParser-targets.cmake") # Report other information. # 现在INCLUDE_DIRS指向include根目录,而非子目录 set(GeoJSONParser_INCLUDE_DIRS "${_prefix}/include") file(GLOB GeoJSONParser_LIBRARIES "${_prefix}/lib/GeoJSONParser-@version@/lib*")
OSMManager的config.cmake.in做同样修改:将OSMManager_INCLUDE_DIRS设置为"${_prefix}/include"。
4. 主项目代码中的引入方式
现在可以像rapidjson那样,通过库名前缀区分同名头文件:
// 引入GeoJSONParser的utils.h #include <GeoJSONParser/utils.h> // 引入OSMManager的utils.h #include <OSMManager/utils.h>
主项目的CMakeLists.txt无需修改,因为库的config文件已经提供了正确的头文件搜索路径。
内容的提问来源于stack exchange,提问作者Andreas Scalas
相关产品推荐
相关产品推荐

