cmake自动添加源文件和头文件

一、背景

项目中需要不断新建源文件和头文件,每添加一个文件CMakeLists.txt中需要手动加入源文件和指定头文件路径,不胜其烦。突发奇想cmake能不能像IDE一样,指定一次源文件和同文件后即可。遂百度查找资料,耗费一天时间,最终实现,整理分享出来。鉴于水平有限,方法不一定最优,欢迎讨论。

二、基本原理

遍历指定路径下的*.cpp *.c文件,将得到的文件列表使用add_library()加入工程。

遍历指定路径下的*.hpp *.h文件,将得到的文件列表使用include_directories()指定头文件路径。

三、CMakeLists.txt文件
1.自动添加源文件
# 查找source目录下的所有*.cpp,*.c源文件,并将文件列表保存到 DIR_LIB_SRCS 变量
file(GLOB_RECURSE SRC_DIR_LIST "source/*.cpp" "source/*.c")	#遍历获取source/路径下所有的*.cpp和*.c文件列表

#将DIR_LIB_SRCS中文件列表以静态库的方式加入工程
add_library(
        ${PROJECT_NAME} STATIC
        ${SRC_DIR_LIST}										#将DIR_LIB_SRCS中文件列表以静态库的方式加入工程
) 

message(STATUS "SRC_DIR_LIST = ${SRC_DIR_LIST}")			#打印SRC_DIR_LIST中列表信息
2.自动添加头文件路径

感谢各位网友在评论区对FIND_INCLUDE_DIR函数的不断完善,现将原函数和评论区中的函数一起整理出来,当前最完善版本为 @登上王者的男人 提供的v3.0版本,建议使用此版本:

v1.0版本

来源:作者原函数

存在问题:头文件路径没有去重

#自动查找头文件路径函数(没有去重)
macro(FIND_INCLUDE_DIR result curdir)  									#定义函数,2个参数:存放结果result;指定路径curdir;
    file(GLOB_RECURSE children "${curdir}/*.hpp" "${curdir}/*.h" )		#遍历获取{curdir}中*.hpp和*.h文件列表
    message(STATUS "children= ${children}")								#打印*.hpp和*.h的文件列表
    set(dirlist "")														#定义dirlist中间变量,并初始化
    foreach(child ${children})											#for循环
        string(REGEX REPLACE "(.*)/.*" "\\1" LIB_NAME ${child})			#字符串替换,用/前的字符替换/*h
        if(IS_DIRECTORY ${LIB_NAME})									#判断是否为路径
            LIST(APPEND dirlist ${LIB_NAME})							#将合法的路径加入dirlist变量中
        endif()															#结束判断
    endforeach()														#结束for循环
    set(${result} ${dirlist})											#dirlist结果放入result变量中
endmacro()																#函数结束



#查找include目录下的所有*.hpp,*.h头文件,并路径列表保存到 INCLUDE_DIR_LIST 变量中
FIND_INCLUDE_DIR(INCLUDE_DIR_LIST ${PROJECT_SOURCE_DIR}/source)			#调用函数,指定参数

#将INCLUDE_DIR_LIST中路径列表加入工程		
include_directories(   													#INCLUDE_DIR_LIST路径列表加入工程
        ${INCLUDE_DIR_LIST}
)

message(STATUS "INCLUDE_DIR_LIST = ${INCLUDE_DIR_LIST}")				#打印INCLUDE_DIR_LIST中列表信息
v2.0版本

来源:评论区 @Derek_996 网友提供

存在问题:去重,但是无法区分字符串相同数字后缀不同的路径:例如/app/test_1和/app/test_2

macro(FIND_INCLUDE_DIR result curdir)  									#定义函数,2个参数:存放结果result;指定路径curdir;
    file(GLOB_RECURSE children "${curdir}/*.hpp" "${curdir}/*.h" )		#遍历获取{curdir}中*.hpp和*.h文件列表
    message(STATUS "children= ${children}")								#打印*.hpp和*.h的文件列表
    set(dirlist "")														#定义dirlist中间变量,并初始化
    foreach(child ${children})											#for循环
        string(REGEX REPLACE "(.*)/.*" "\\1" LIB_NAME ${child})			#字符串替换,用/前的字符替换/*h
        if(IS_DIRECTORY ${LIB_NAME})									#判断是否为路径
            if(dirlist MATCHES ${LIB_NAME})								#去重,判断dirlist中是否有字符串和${LIB_NAME}匹配,无法区分字符串相同数字后缀不同的路径:例如/app/test_1和/app/test_2
            else()
              MESSAGE(STATUS "current platform: Linux ")
              LIST(APPEND dirlist ${LIB_NAME})							#将合法的路径加入dirlist变量中  
            endif()
        endif()															#结束判断
    endforeach()														#结束for循环
    set(${result} ${dirlist})											#dirlist结果放入result变量中
endmacro()
v3.0版本

来源:评论区 @登上王者的男人 网友提供

存在问题:去重,当前最完善版本。可以区分字符串相同数字后缀不同的路径:例如/app/test_1和/app/test_2

macro(FIND_INCLUDE_DIR result curdir)									#定义函数,2个参数:存放结果result;指定路径curdir;
	file(GLOB_RECURSE children "${curdir}/*.hpp" "${curdir}/*.h" )		#遍历获取{curdir}中*.hpp和*.h文件列表
	set(dirlist "")														#定义dirlist中间变量,并初始化
	foreach(child ${children})											#for循环
		string(REGEX REPLACE "(.*)/.*" "\\1" LIB_NAME ${child})			#字符串替换,用/前的字符替换/*h
		if(IS_DIRECTORY ${LIB_NAME})									#判断是否为路径
			list (FIND dirlist ${LIB_NAME} list_index)					#去重,查找dirlist中是否有${LIB_NAME}指定的值,可以区分字符串相同数子后缀不同的路径:例如/app/test_1和/app/test_2
			if(${list_index} LESS 0)									#若没找到则代表列表中没有该路径
				LIST(APPEND dirlist ${LIB_NAME})						#将合法的路径加入dirlist变量中  
			endif()														#结束判断
		endif()															
	endforeach()														#结束for循环
	set(${result} ${dirlist})											#dirlist结果放入result变量中
endmacro()
四、调试总结

1.FIND_INCLUDE_DIR()调用时要指定绝对路径