CMake 秘籍(八)(1)https://developer.aliyun.com/article/1525074
然后,我们定义所需的源文件:
add_library(basic_sources "") target_sources(basic_sources PRIVATE arabic.c beval.c blowfish.c buffer.c charset.c crypt.c crypt_zip.c dict.c diff.c digraph.c edit.c eval.c evalfunc.c ex_cmds.c ex_cmds2.c ex_docmd.c ex_eval.c ex_getln.c farsi.c fileio.c fold.c getchar.c hardcopy.c hashtab.c if_cscope.c if_xcmdsrv.c json.c list.c main.c mark.c memfile.c memline.c menu.c message.c misc1.c misc2.c move.c mbyte.c normal.c ops.c option.c os_unix.c auto/pathdef.c popupmnu.c pty.c quickfix.c regexp.c screen.c search.c sha256.c spell.c spellfile.c syntax.c tag.c term.c terminal.c ui.c undo.c userfunc.c version.c window.c ) target_include_directories(basic_sources PRIVATE ${CMAKE_CURRENT_LIST_DIR}/proto ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) target_compile_definitions(basic_sources PRIVATE "HAVE_CONFIG_H" ) target_link_libraries(vim PUBLIC basic_sources )
然后,我们定义可选的源文件:
add_library(extra_sources "") if(ENABLE_NETBEANS) target_sources(extra_sources PRIVATE netbeans.c ) endif() if(ENABLE_CHANNEL) target_sources(extra_sources PRIVATE channel.c ) endif() target_include_directories(extra_sources PUBLIC ${CMAKE_CURRENT_LIST_DIR}/proto ${CMAKE_CURRENT_BINARY_DIR} ) target_compile_definitions(extra_sources PRIVATE "HAVE_CONFIG_H" ) target_link_libraries(vim PUBLIC extra_sources )
该文件还选择性地处理并链接 src/libvterm/
,使用以下代码:
if(ENABLE_TERMINAL) add_subdirectory(libvterm) target_link_libraries(vim PUBLIC libvterm ) endif()
相应的 src/libvterm/CMakeLists.txt
包含以下内容:
add_library(libvterm "") target_sources(libvterm PRIVATE src/encoding.c src/keyboard.c src/mouse.c src/parser.c src/pen.c src/screen.c src/state.c src/unicode.c src/vterm.c ) target_include_directories(libvterm PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include ) target_compile_definitions(libvterm PRIVATE "HAVE_CONFIG_H" "INLINE=" "VSNPRINTF=vim_vsnprintf" "IS_COMBINING_FUNCTION=utf_iscomposing_uint" "WCWIDTH_FUNCTION=utf_uint2cells" )
我们已经从记录的 build.log
中提取了编译定义。树形结构的优点是目标定义靠近源文件所在的位置。如果我们决定重构代码并重命名或移动目录,描述目标的 CMake 文件有机会随源文件一起移动。
我们的示例代码甚至还没有配置(除非在成功的 Autotools 构建之后尝试):
$ mkdir -p build $ cd build $ cmake .. -- The C compiler identification is GNU 8.2.0 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Detecting C compile features -- Detecting C compile features - done -- Configuring done CMake Error at src/CMakeLists.txt:12 (add_library): Cannot find source file: auto/pathdef.c Tried extensions .c .C .c++ .cc .cpp .cxx .cu .m .M .mm .h .hh .h++ .hm .hpp .hxx .in .txx
我们需要生成 auto/pathdef.c
(以及其他文件),我们将在下一节中考虑这一点。
生成文件和编写平台检查
事实证明,对于 Vim 代码示例,我们需要在配置时生成三个文件:src/auto/pathdef.c
、src/auto/config.h
和 src/auto/osdef.h
:
pathdef.c
记录安装路径、编译和链接标志、编译代码的用户以及主机名config.h
包含特定于系统环境的编译定义osdef.h
是一个包含由src/osdef.sh
生成的编译定义的文件。
这种情况相当常见。我们需要根据 CMake 变量配置一个文件,执行一系列平台检查以生成 config.h
,并在配置时执行一个脚本。特别是,平台检查对于追求可移植性的项目来说非常常见,以适应操作系统之间的微妙差异。
在原始布局中,文件在 src
文件夹下生成。我们不喜欢这种方法,在我们的示例 CMake 移植中将采取不同的做法:这些文件将在构建目录中生成。这样做的原因是,生成的文件通常依赖于所选的选项、编译器或构建类型,我们希望保持能够配置多个具有相同源代码的构建的能力。为了在构建目录中启用生成,我们将不得不对之前列出的文件之一的生成脚本进行最小程度的更改。
如何组织文件
我们将收集生成这些文件的函数在src/autogenerate.cmake
中,包含此模块,并在定义可执行目标之前在src/CMakeLists.txt
中调用这些函数:
# generate config.h, pathdef.c, and osdef.h include(autogenerate.cmake) generate_config_h() generate_pathdef_c() generate_osdef_h() add_executable(vim main.c ) # ...
包含的src/autogenerate.cmake
包含其他包含功能,我们将需要这些功能来探测头文件,函数和库,以及三个函数:
include(CheckTypeSize) include(CheckFunctionExists) include(CheckIncludeFiles) include(CheckLibraryExists) include(CheckCSourceCompiles) function(generate_config_h) # ... to be written endfunction() function(generate_pathdef_c) # ... to be written endfunction() function(generate_osdef_h) # ... to be written endfunction()
我们选择使用函数生成文件,而不是宏或“裸”CMake 代码。正如我们在前几章中讨论的那样,这避免了许多陷阱:
- 它使我们能够避免文件被多次生成,以防我们不小心多次包含该模块。如第五章中的重新定义函数和宏所述,在第七章,项目结构中,我们可以使用包含保护来防止不小心多次运行代码。
- 它确保完全控制函数内部定义的变量的作用域。这避免了这些定义泄漏并污染主作用域。
CMake 秘籍(八)(3)https://developer.aliyun.com/article/1525076