使用CMake为每一个C文件生成可执行对象
由于CLion似乎对CMake的友好度更高,所以将原有的使用Makefile的工作方式改为CMake。 原项目结构如下:
how2heap/
Makefile
{source_files}.c
...
glibc_2.25/
{source_files}.c
glibc_2.26/
{source_files}.c
原有的Makefile内容如下,功能就是为每个C源文件单独生成一个可执行文件。
BASE = fastbin_dup malloc_playground first_fit calc_tcache_idx
V2.25 = glibc_2.25/fastbin_dup_into_stack glibc_2.25/fastbin_dup_consolidate glibc_2.25/unsafe_unlink glibc_2.25/house_of_spirit glibc_2.25/poison_null_byte glibc_2.25/house_of_lore glibc_2.25/overlapping_chunks glibc_2.25/overlapping_chunks_2 glibc_2.25/house_of_force glibc_2.25/large_bin_attack glibc_2.25/unsorted_bin_attack glibc_2.25/unsorted_bin_into_stack glibc_2.25/house_of_einherjar glibc_2.25/house_of_orange
V2.26 = glibc_2.26/unsafe_unlink glibc_2.26/house_of_lore glibc_2.26/overlapping_chunks glibc_2.26/large_bin_attack glibc_2.26/unsorted_bin_attack glibc_2.26/unsorted_bin_into_stack glibc_2.26/house_of_einherjar glibc_2.26/tcache_dup glibc_2.26/tcache_poisoning glibc_2.26/tcache_house_of_spirit
PROGRAMS = $(BASE) $(V2.25) $(V2.26)
CFLAGS += -std=c99 -g
# Convenience to auto-call mcheck before the first malloc()
#CFLAGS += -lmcheck
all: $(PROGRAMS)
clean:
rm -f $(PROGRAMS)
如果要使用CMake的写法,那么有下面几步:
-
在所有存在源代码的目录下创建一个CMakeLists.txt,比如在how2heap,glibc_2.25和glibc_2.26这三个文件夹下各创建一个CMakeLists.txt。
- 编辑每个CMakeLists.txt,用来指导生成所在文件夹源文件对应的可执行文件。
- 使用CLion进行build等操作。
显然重点是第2步,下面分层次对CMakeLists.txt进行说明。
顶层CMakeLists.txt
项目根目录下的CMakeLists.txt如下所示:
project(how2heap)
cmake_minimum_required(VERSION 3.14)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -g")
set(CMAKE_VERBOSE_MAKEFILE TRUE)
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -lmcheck")
aux_source_directory(. topdir)
foreach(src ${topdir})
string(REGEX MATCH "[A-Za-z0-9_]+" exe ${src})
add_executable(${exe} ${src})
endforeach()
add_subdirectory(glibc_2.25)
add_subdirectory(glibc_2.26)
首先简单说明CMakeLists.txt中的变量定义与使用方法,变量定义直接写标识符即可,变量引用要写成${变量}这种形式。
只说重点,其中aux_source_directory
用于将本CMakeLists.txt所在文件夹.
(不包括子文件夹)下的所有源文件的路径以字符串构成的列表形式存进变量topdir
。
foreach
…endforeach
语句写成Python就是for src in topdir
,string
函数正则匹配了源文件名无后缀的内容并赋值给变量exe
。
add_executable
将可执行目标文件名(项目内不可重名)以及基于的源文件名注册进当前项目。
add_subdirectory
表示CMake会处理指定的文件夹中的CMakeLists.txt。
子CMakeLists.txt
- 文件夹glibc_2.25中的CMakeLists.txt如下,相比顶层的CMakeLists.txt简洁许多。
aux_source_directory(. v25dir) # v26dir in glibc_2.26
foreach(src ${v25dir})
string(REGEX REPLACE "./([A-Za-z0-9_]+).c" "\\1_v25" exe ${src})
add_executable(${exe} ${src})
endforeach()
由于在CMakeLists.txt中定义的变量似乎都是全局变量(来自经验,未查证),所以如果在这里aux_source_directory
中还是写topdir而不是换一个名字比如v25dir,那么CMake会报类似找不到fastbin_dup.c
(项目根目录下的一个文件)这种错误。
string
函数选择了正则模式的替换功能:
-
第一个正则式匹配了整个src变量,并且使用
()
语法将需要的可执行文件名提取了出来。 -
第二个正则式使用
\\1
引用了上面使用()
提取出来的内容,然后加上后缀。(由于glibc_2.25和glibc_2.26文件夹中存在很多文件名完全相同的文件,而add_executable
不允许重名,所以分别在末尾加上_v25
、_v26
加以区别而不是像顶层的CMakeLists.txt那样进行简单的匹配。)
- 文件夹glibc_2.26中的CMakeLists.txt类似,进行相应修改即可。
至此应该没有问题,可以使用CLion来build以及clean了。