如何基于CMake实现C++公共头文件的合法包含检查?
管控C++公共头文件包含规则的CMake实现方案
针对你提出的需求——仅允许*_public.h结尾的头文件包含其他同规则的公共头文件,禁止包含非公共头文件,以下提供两种可落地的CMake方案,优先推荐你倾向的编译检查方案。
方案一:编译检查方案(准确覆盖所有场景)
核心思路是创建一个专门的检查目标,仅让该目标能访问公共头文件,编译时若公共头文件包含了非公共头文件,会直接触发“文件未找到”的编译错误。
步骤1:收集所有公共头文件
在项目根目录的CMakeLists.txt中,递归查找所有符合*_public.h模式的文件:
file(GLOB_RECURSE PUBLIC_HEADERS "${PROJECT_SOURCE_DIR}/*_public.h")
步骤2:复制公共头文件到临时检查目录
创建一个临时目录,将所有公共头文件按原目录结构复制进去,确保包含路径的一致性:
set(CHECK_DIR "${CMAKE_BINARY_DIR}/public_header_check") file(COPY ${PUBLIC_HEADERS} DESTINATION ${CHECK_DIR} FILES_MATCHING PATTERN "*_public.h")
步骤3:生成检查用的C++文件
先创建一个模板文件public_header_check.cpp.in(放在项目根目录):
@FOREACH(rel_header IN LISTS RELATIVE_HEADERS) #include "@rel_header@" @ENDFOREACH@
然后在CMake中处理公共头文件的相对路径,生成实际的检查cpp文件:
set(RELATIVE_HEADERS "") foreach(header IN LISTS PUBLIC_HEADERS) file(RELATIVE_PATH rel_header "${PROJECT_SOURCE_DIR}" "${header}") list(APPEND RELATIVE_HEADERS "${rel_header}") endforeach() configure_file( "${PROJECT_SOURCE_DIR}/public_header_check.cpp.in" "${CHECK_DIR}/public_header_check.cpp" @ONLY )
步骤4:创建检查目标并关联主项目
生成一个不参与最终发布的检查目标,设置其仅能访问临时检查目录的文件:
add_executable(public_header_check EXCLUDE_FROM_ALL "${CHECK_DIR}/public_header_check.cpp") target_include_directories(public_header_check PRIVATE "${CHECK_DIR}")
让主项目依赖这个检查目标,确保每次构建主项目前先执行检查:
# 替换成你的主项目目标名 add_dependencies(myProject public_header_check)
这样只要*_public.h中包含了非公共头文件,编译检查目标时就会抛出“找不到文件”的错误,阻断后续构建。
方案二:文本扫描方案(轻量高效)
如果项目中没有宏包裹、条件编译等复杂的#include场景,可以直接扫描公共头文件的文本内容,匹配所有包含语句并检查规则。
在CMakeLists.txt中添加以下代码:
file(GLOB_RECURSE PUBLIC_HEADERS "${PROJECT_SOURCE_DIR}/*_public.h") foreach(header IN LISTS PUBLIC_HEADERS) file(READ "${header}" HEADER_CONTENT) # 匹配所有#include "xxx"格式的语句 string(REGEX MATCHALL "#include \"([^\"]+)\"" INCLUDE_STATEMENTS "${HEADER_CONTENT}") foreach(INCLUDE_STMT IN LISTS INCLUDE_STATEMENTS) # 提取包含的文件名 string(REGEX REPLACE "#include \"([^\"]+)\"" "\\1" INCLUDE_FILE "${INCLUDE_STMT}") # 检查是否符合公共头文件规则 string(FIND "${INCLUDE_FILE}" "_public.h" PUBLIC_SUFFIX_POS) if(PUBLIC_SUFFIX_POS EQUAL -1) message(FATAL_ERROR "非法包含:公共头文件 ${header} 引用了非公共头文件 ${INCLUDE_FILE}") endif() endforeach() endforeach()
该方案无需编译,直接在CMake配置阶段完成检查,速度更快,但无法处理动态生成的包含语句(如宏定义的文件名)。
内容的提问来源于stack exchange,提问作者Efe Balo
相关产品推荐
相关产品推荐

