CMake 秘籍(八)(2)

简介: CMake 秘籍(八)

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.csrc/auto/config.hsrc/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

相关文章
|
6月前
|
编译器 Shell
CMake 秘籍(八)(3)
CMake 秘籍(八)
40 2
|
6月前
|
编译器 Shell 开发工具
CMake 秘籍(八)(5)
CMake 秘籍(八)
36 2
|
6月前
|
编译器 Linux C++
CMake 秘籍(六)(5)
CMake 秘籍(六)
36 1
|
6月前
|
Shell Linux C++
CMake 秘籍(六)(4)
CMake 秘籍(六)
49 1
|
6月前
|
消息中间件 Unix C语言
CMake 秘籍(二)(5)
CMake 秘籍(二)
139 1
|
6月前
|
Linux iOS开发 C++
CMake 秘籍(六)(3)
CMake 秘籍(六)
48 1
|
6月前
|
编译器 开发工具 git
CMake 秘籍(八)(1)
CMake 秘籍(八)
28 1
|
6月前
|
Linux C++ iOS开发
CMake 秘籍(三)(4)
CMake 秘籍(三)
40 1
|
6月前
|
并行计算 关系型数据库 编译器
CMake 秘籍(七)(3)
CMake 秘籍(七)
88 0
|
6月前
|
Linux C++ iOS开发
CMake 秘籍(四)(3)
CMake 秘籍(四)
24 0