CMake 秘籍(八)(3)https://developer.aliyun.com/article/1525076
检测所需依赖项和链接
现在我们已经将所有生成的文件放置到位,让我们重新尝试构建。我们应该能够配置和编译源代码,但我们无法链接:
$ mkdir -p build $ cd build $ cmake .. $ cmake --build . ... Scanning dependencies of target vim [ 98%] Building C object src/CMakeFiles/vim.dir/main.c.o [100%] Linking C executable ../bin/vim ../lib64/libbasic_sources.a(term.c.o): In function `set_shellsize.part.12': term.c:(.text+0x2bd): undefined reference to `tputs' ../lib64/libbasic_sources.a(term.c.o): In function `getlinecol': term.c:(.text+0x902): undefined reference to `tgetent' term.c:(.text+0x915): undefined reference to `tgetent' term.c:(.text+0x935): undefined reference to `tgetnum' term.c:(.text+0x948): undefined reference to `tgetnum' ... many other undefined references ...
同样,我们可以从 Autotools 编译的日志文件中,特别是链接行中获得灵感,通过在src/CMakeLists.txt
中添加以下代码来解决缺失的依赖:
# find X11 and link to it find_package(X11 REQUIRED) if(X11_FOUND) target_link_libraries(vim PUBLIC ${X11_LIBRARIES} ) endif() # a couple of more system libraries that the code requires foreach(_library IN ITEMS Xt SM m tinfo acl gpm dl) find_library(_${_library}_found ${_library} REQUIRED) if(_${_library}_found) target_link_libraries(vim PUBLIC ${_library} ) endif() endforeach()
注意我们是如何一次向目标添加一个库依赖,而不必构建和携带一个变量中的库列表,这会导致更脆弱的 CMake 代码,因为变量在过程中可能会被破坏,尤其是在大型项目中。
通过这个更改,代码编译并链接:
$ cmake --build . ... Scanning dependencies of target vim [ 98%] Building C object src/CMakeFiles/vim.dir/main.c.o [100%] Linking C executable ../bin/vim [100%] Built target vim
我们现在可以尝试执行编译后的二进制文件,并用我们新编译的 Vim 版本编辑一些文件!
重现编译器标志
现在让我们尝试调整编译器标志以反映参考构建。
定义编译器标志
到目前为止,我们还没有定义任何自定义编译器标志,但从参考 Autotools 构建中,我们记得代码是用-g -U_FORTIFY_SOURCE
编译的
-D_FORTIFY_SOURCE=1 -O2
使用 GNU C 编译器。
我们的第一个方法可能是定义以下内容:
if(CMAKE_C_COMPILER_ID MATCHES GNU) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -O2") endif()
而且,我们会将这段代码放在src/CMakeLists.txt
的顶部,就在生成源文件之前(因为pathdef.c
使用了${CMAKE_C_FLAGS}
):
# <- we will define flags right here include(autogenerate.cmake) generate_config_h() generate_pathdef_c() generate_osdef_h()
对编译器标志定义的一个小改进是将-O2
定义为Release
配置标志,并为Debug
配置关闭优化:
if(CMAKE_C_COMPILER_ID MATCHES GNU) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1") set(CMAKE_C_FLAGS_RELEASE "-O2") set(CMAKE_C_FLAGS_DEBUG "-O0") endif()
请使用make VERBOSE=1
验证构建是否使用了预期的标志。
编译器标志的范围
在这个特定的示例项目中,所有源文件使用相同的编译标志。对于其他项目,我们可能更倾向于不全局定义编译标志,而是使用target_compile_options
为每个目标单独定义标志。这样做的好处是更灵活和更局部的范围。在我们这里的例子中,代价可能是不必要的代码重复。
移植测试
现在让我们讨论如何将测试从参考构建移植到我们的 CMake 构建。
开始
如果正在移植的项目包含测试目标或任何形式的自动化测试或测试脚本,第一步将再次是运行传统的测试步骤并记录使用的命令。对于 Vim 项目,起点是src/testdir/Makefile
。在 CMake 侧定义测试可能是有意义的,接近src/testdir/Makefile
和测试脚本,我们将选择在src/testdir/CMakeLists.txt
中定义测试。为了处理这样的文件,我们必须在其src/CMakeLists.txt
中引用它:
add_subdirectory(testdir)
我们还应该在顶层CMakeLists.txt
中启用测试目标,就在处理src/CMakeLists.txt
之前:
# enable the test target enable_testing() # process src/CMakeLists.txt in its own scope add_subdirectory(src)
到目前为止,在我们向src/testdir/CMakeLists.txt
填充add_test
指令之前,测试目标还是空的。add_test
中最少需要指定的是测试名称和一个运行命令。该命令可以是任何语言编写的任何脚本。对于 CMake 来说,关键的是如果测试成功,脚本返回零,如果测试失败,则返回非零。更多详情,我们请读者参考第四章,创建和运行测试。对于 Vim 的情况,我们需要更多来适应多步骤测试,我们将在下一节讨论。
CMake 秘籍(八)(5)https://developer.aliyun.com/article/1525078