如何将子项目的条件预处理宏定义传递给父项目?
如何将子项目的预处理宏同步到父项目
问题场景
需要将子项目中由CMake选项条件生成的预处理宏同步到父项目,以下是最简复现示例:
文件结构
./CMakeLists.txt .......父项目CMake配置(A) parent.cpp .......父项目源码(B) sub/CMakeLists.txt ...子项目CMake配置(C) sub.hpp ..........子项目头文件(D) sub.cpp ..........子项目源码(E)
父项目CMakeLists.txt(A)
cmake_minimum_required(VERSION 3.10) project(parent) add_subdirectory(sub) include_directories(sub) add_executable(parent parent.cpp)
parent.cpp(B)
#include "sub.hpp" int main() { print(); }
子项目CMakeLists.txt(C)
cmake_minimum_required(VERSION 3.10) project(sub) message(STATUS ${OPTION1}) add_executable(sub sub.cpp) if (OPTION1) target_compile_definitions(sub PUBLIC PP_OPTION1) endif ()
sub.hpp(D)
#include <iostream> inline void print() { #if defined(PP_OPTION1) std::cout << "PP_OPTION1 set" << std::endl; #else std::cout << "PP_OPTION1 not set" << std::endl; #endif }
sub.cpp(E)
#include "sub.hpp" int main() { print(); }
执行步骤与现象
执行以下命令:
mkdir build cd build cmake -DOPTION1=ON .. cmake --build .
生成parent和sub/sub两个二进制文件:
- 运行
parent输出:PP_OPTION1 not set - 运行
sub/sub输出:PP_OPTION1 set
原因是PP_OPTION1仅对子项目的sub目标生效,父项目的parent目标未同步该宏定义。临时方案可通过-DCMAKE_CXX_FLAGS="-DPP_OPTION1"手动传递,但希望能自动同步由CMake选项条件定义的预处理宏。
解决方案
方法一:子项目通过INTERFACE目标导出编译定义
这是CMake推荐的现代做法,通过创建INTERFACE库目标统一导出公共配置,父项目链接后自动继承所有INTERFACE/PUBLIC级别的编译定义、头文件路径等。
修改子项目sub/CMakeLists.txt:
cmake_minimum_required(VERSION 3.10) project(sub) message(STATUS ${OPTION1}) # 创建INTERFACE库,用于导出公共配置 add_library(sub_interface INTERFACE) if (OPTION1) target_compile_definitions(sub_interface INTERFACE PP_OPTION1) endif () # 导出头文件路径,替代父项目的include_directories target_include_directories(sub_interface INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) # 子项目自身可执行文件链接该目标,确保拿到宏定义 add_executable(sub sub.cpp) target_link_libraries(sub PRIVATE sub_interface)
修改父项目CMakeLists.txt:
cmake_minimum_required(VERSION 3.10) project(parent) add_subdirectory(sub) add_executable(parent parent.cpp) # 链接子项目的INTERFACE目标,自动继承配置 target_link_libraries(parent PRIVATE sub_interface)
方法二:父项目直接同步宏定义逻辑
因为OPTION1是全局CMake变量,父项目可直接读取该变量,复用子项目的宏定义条件:
修改父项目CMakeLists.txt:
cmake_minimum_required(VERSION 3.10) project(parent) add_subdirectory(sub) include_directories(sub) add_executable(parent parent.cpp) # 同步子项目的宏定义逻辑 if (OPTION1) target_compile_definitions(parent PRIVATE PP_OPTION1) endif ()
这种方式简单直接,但需要父项目与子项目保持宏定义逻辑一致,若子项目修改条件或宏名,父项目需同步调整。
方法三:子项目传递宏状态缓存变量
若子项目宏定义逻辑复杂,或不想让父项目依赖OPTION1变量,可让子项目将宏的最终状态设为缓存变量,父项目读取后应用:
修改子项目sub/CMakeLists.txt:
cmake_minimum_required(VERSION 3.10) project(sub) message(STATUS ${OPTION1}) add_executable(sub sub.cpp) # 定义缓存变量传递宏状态 set(SUB_HAS_PP_OPTION1 OFF CACHE BOOL "Whether PP_OPTION1 is defined in sub project") if (OPTION1) target_compile_definitions(sub PUBLIC PP_OPTION1) set(SUB_HAS_PP_OPTION1 ON CACHE BOOL "Whether PP_OPTION1 is defined in sub project" FORCE) endif ()
修改父项目CMakeLists.txt:
cmake_minimum_required(VERSION 3.10) project(parent) add_subdirectory(sub) include_directories(sub) add_executable(parent parent.cpp) if (SUB_HAS_PP_OPTION1) target_compile_definitions(parent PRIVATE PP_OPTION1) endif ()
这种方式解耦了父项目与子项目的具体选项名称,父项目仅需关心子项目最终是否启用目标宏。
内容的提问来源于stack exchange,提问作者Takatoshi Kondo
相关产品推荐
相关产品推荐

