从qmake转CMake:如何实现调试与发布模式的依赖管理?
完全可以复现你描述的qmake依赖管理模式,核心是利用CMake的子项目构建和外部依赖导入分别对应调试/发布阶段,同时通过CMake内置路径变量解决include基准问题。
一、先解决路径适配问题
不要直接用相对路径引用.cmake文件,改用CMake的CMAKE_CURRENT_LIST_DIR变量(指向当前处理的CMakeLists.txt所在目录)拼接绝对路径,彻底规避基准路径混乱:
假设项目结构如下:
project_root/ ├─ shared/ │ └─ CMakeLists.txt └─ app/ ├─ common/ │ └─ CMakeLists.txt └─ CMakeLists.txt
在app/CMakeLists.txt中引用shared的配置时,写法如下:
include("${CMAKE_CURRENT_LIST_DIR}/../shared/CMakeLists.txt")
更灵活的方式是在根目录CMakeLists.txt中定义全局根路径变量:
set(PROJECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR})
后续所有子项目都用${PROJECT_ROOT_DIR}/shared/CMakeLists.txt这类写法引用,完全消除相对路径歧义。
二、调试阶段:自动同步编译依赖(对应qmake include .pri)
直接把shared和common作为当前项目的子目录添加,CMake会自动在编译应用目标前先编译依赖库:
在app/CMakeLists.txt中添加:
# 添加shared静态库子项目,指定独立构建目录避免文件混乱 add_subdirectory(${PROJECT_ROOT_DIR}/shared ${CMAKE_BINARY_DIR}/shared_build) # 添加app专属common静态库子项目 add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/common ${CMAKE_BINARY_DIR}/common_build) # 链接应用目标到依赖库 add_executable(app_standalone main.cpp) target_link_libraries(app_standalone PRIVATE shared common) add_library(app_plugin1 MODULE plugin1.cpp) target_link_libraries(app_plugin1 PRIVATE shared common)
这样只要编译应用的任意目标,CMake会自动检查shared和common是否需要重新编译,完全复现qmake中.pri文件的同步编译效果。
三、发布阶段:先编译库再链接(对应构建脚本预编译)
先单独构建shared和common并安装到指定目录,再让应用项目导入预编译库:
- 给库添加安装规则
在shared/CMakeLists.txt和app/common/CMakeLists.txt末尾添加安装逻辑:
# shared的CMakeLists.txt install(TARGETS shared ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) install(FILES shared.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include) # common的CMakeLists.txt install(TARGETS common ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) install(FILES common.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include)
然后用命令预编译安装:
# 编译并安装shared cmake -S ./shared -B ./shared_build -DCMAKE_INSTALL_PREFIX=./install_dir cmake --build ./shared_build --target install # 编译并安装common cmake -S ./app/common -B ./common_build -DCMAKE_INSTALL_PREFIX=./install_dir cmake --build ./common_build --target install
- 应用项目导入预编译库
在app/CMakeLists.txt中通过find_library直接定位预编译库:
# 定位预编译的shared和common库 find_library(SHARED_LIB NAMES shared PATHS ${INSTALL_DIR}/lib NO_DEFAULT_PATH) find_library(COMMON_LIB NAMES common PATHS ${INSTALL_DIR}/lib NO_DEFAULT_PATH) # 链接应用目标 add_executable(app_standalone main.cpp) target_include_directories(app_standalone PRIVATE ${INSTALL_DIR}/include) target_link_libraries(app_standalone PRIVATE ${SHARED_LIB} ${COMMON_LIB})
如果给库配置了Config.cmake文件(更规范的CMake依赖方式),也可以用find_package导入:
find_package(shared REQUIRED PATHS ${INSTALL_DIR}) find_package(common REQUIRED PATHS ${INSTALL_DIR}) add_executable(app_standalone main.cpp) target_link_libraries(app_standalone PRIVATE shared::shared common::common)
四、用CMake选项一键切换调试/发布模式
添加配置选项,无需手动修改CMakeLists.txt即可切换两种模式:
在app/CMakeLists.txt开头添加:
option(BUILD_DEPENDENCIES_INTERNAL "Build shared and common as subprojects (debug mode)" ON) set(INSTALL_DIR "${PROJECT_ROOT_DIR}/install_dir" CACHE PATH "Path to prebuilt shared/common libraries (release mode)") if(BUILD_DEPENDENCIES_INTERNAL) # 调试模式:自动编译依赖 add_subdirectory(${PROJECT_ROOT_DIR}/shared ${CMAKE_BINARY_DIR}/shared_build) add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/common ${CMAKE_BINARY_DIR}/common_build) else() # 发布模式:导入预编译库 find_library(SHARED_LIB NAMES shared PATHS ${INSTALL_DIR}/lib NO_DEFAULT_PATH) find_library(COMMON_LIB NAMES common PATHS ${INSTALL_DIR}/lib NO_DEFAULT_PATH) # 将预编译库声明为CMake目标,统一后续链接逻辑 add_library(shared STATIC IMPORTED) set_target_properties(shared PROPERTIES IMPORTED_LOCATION ${SHARED_LIB}) target_include_directories(shared INTERFACE ${INSTALL_DIR}/include) add_library(common STATIC IMPORTED) set_target_properties(common PROPERTIES IMPORTED_LOCATION ${COMMON_LIB}) target_include_directories(common INTERFACE ${INSTALL_DIR}/include) endif() # 后续所有应用目标的链接逻辑完全统一 add_executable(app_standalone main.cpp) target_link_libraries(app_standalone PRIVATE shared common) add_library(app_plugin1 MODULE plugin1.cpp) target_link_libraries(app_plugin1 PRIVATE shared common)
调试时默认启用BUILD_DEPENDENCIES_INTERNAL=ON,自动同步编译依赖;发布时只需通过命令行传入参数切换:
cmake -S ./app -B ./app_build -DBUILD_DEPENDENCIES_INTERNAL=OFF -DINSTALL_DIR=./install_dir cmake --build ./app_build
内容的提问来源于stack exchange,提问作者Nuno Santos

