CMake 秘籍(八)(2)https://developer.aliyun.com/article/1525075
根据系统环境配置预处理器定义
config.h
文件是从src/config.h.in
生成的,其中包含根据系统能力配置的预处理器标志:
/* Define if we have EBCDIC code */ #undef EBCDIC /* Define unless no X support found */ #undef HAVE_X11 /* Define when terminfo support found */ #undef TERMINFO /* Define when termcap.h contains ospeed */ #undef HAVE_OSPEED /* ... */
从src/config.h
生成的示例可以像这个示例一样开始(定义可能因环境而异):
/* Define if we have EBCDIC code */ /* #undef EBCDIC */ /* Define unless no X support found */ #define HAVE_X11 1 /* Define when terminfo support found */ #define TERMINFO 1 /* Define when termcap.h contains ospeed */ /* #undef HAVE_OSPEED */ /* ... */
平台检查的一个很好的资源是这个页面:www.vtk.org/Wiki/CMake:How_To_Write_Platform_Checks
。
在src/configure.ac
中,我们可以检查需要执行哪些平台检查以设置相应的预处理器定义。
我们将使用#cmakedefine
(cmake.org/cmake/help/v3.5/command/configure_file.html?highlight=cmakedefine
),并确保我们不会破坏现有的 Autotools 构建,我们将复制config.h.in
到config.h.cmake.in
,并将所有#undef SOME_DEFINITION
更改为#cmakedefine SOME_DEFINITION @SOME_DEFINITION@
。
在generate_config_h
函数中,我们首先定义一些变量:
set(TERMINFO 1) set(UNIX 1) # this is hardcoded to keep the discussion in the book chapter # which describes the migration to CMake simpler set(TIME_WITH_SYS_TIME 1) set(RETSIGTYPE void) set(SIGRETURN return) find_package(X11) set(HAVE_X11 ${X11_FOUND})
然后,我们执行一些类型大小检查:
check_type_size("int" VIM_SIZEOF_INT) check_type_size("long" VIM_SIZEOF_LONG) check_type_size("time_t" SIZEOF_TIME_T) check_type_size("off_t" SIZEOF_OFF_T)
然后,我们遍历函数并检查系统是否能够解析它们:
foreach( _function IN ITEMS fchdir fchown fchmod fsync getcwd getpseudotty getpwent getpwnam getpwuid getrlimit gettimeofday getwd lstat memset mkdtemp nanosleep opendir putenv qsort readlink select setenv getpgid setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction sigprocmask sigvec strcasecmp strerror strftime stricmp strncasecmp strnicmp strpbrk strtol towlower towupper iswupper usleep utime utimes mblen ftruncate ) string(TOUPPER "${_function}" _function_uppercase) check_function_exists(${_function} HAVE_${_function_uppercase}) endforeach()
我们验证特定的库是否包含特定的函数:
check_library_exists(tinfo tgetent "" HAVE_TGETENT) if(NOT HAVE_TGETENT) message(FATAL_ERROR "Could not find the tgetent() function. You need to install a terminal library; for example ncurses.") endif()
然后,我们遍历头文件并检查它们是否可用:
foreach( _header IN ITEMS setjmp.h dirent.h stdint.h stdlib.h string.h sys/select.h sys/utsname.h termcap.h fcntl.h sgtty.h sys/ioctl.h sys/time.h sys/types.h termio.h iconv.h inttypes.h langinfo.h math.h unistd.h stropts.h errno.h sys/resource.h sys/systeminfo.h locale.h sys/stream.h termios.h libc.h sys/statfs.h poll.h sys/poll.h pwd.h utime.h sys/param.h libintl.h libgen.h util/debug.h util/msg18n.h frame.h sys/acl.h sys/access.h sys/sysinfo.h wchar.h wctype.h ) string(TOUPPER "${_header}" _header_uppercase) string(REPLACE "/" "_" _header_normalized "${_header_uppercase}") string(REPLACE "." "_" _header_normalized "${_header_normalized}") check_include_files(${_header} HAVE_${_header_normalized}) endforeach()
然后,我们将 CMake 选项从主CMakeLists.txt
转换为预处理器定义:
string(TOUPPER "${FEATURES}" _features_upper) set(FEAT_${_features_upper} 1) set(FEAT_NETBEANS_INTG ${ENABLE_NETBEANS}) set(FEAT_JOB_CHANNEL ${ENABLE_CHANNEL}) set(FEAT_TERMINAL ${ENABLE_TERMINAL})
最后,我们检查是否能够编译特定的代码片段:
check_c_source_compiles( " #include <sys/types.h> #include <sys/stat.h> int main () { struct stat st; int n; stat(\"/\", &st); n = (int)st.st_blksize; ; return 0; } " HAVE_ST_BLKSIZE )
然后使用定义的变量来配置src/config.h.cmake.in
到config.h
,这完成了generate_config_h
函数:
configure_file( ${CMAKE_CURRENT_LIST_DIR}/config.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/auto/config.h @ONLY )
使用路径和编译器标志配置文件
我们生成pathdef.c
从src/pathdef.c.in
:
#include "vim.h" char_u *default_vim_dir = (char_u *)"@_default_vim_dir@"; char_u *default_vimruntime_dir = (char_u *)"@_default_vimruntime_dir@"; char_u *all_cflags = (char_u *)"@_all_cflags@"; char_u *all_lflags = (char_u *)"@_all_lflags@"; char_u *compiled_user = (char_u *)"@_compiled_user@"; char_u *compiled_sys = (char_u *)"@_compiled_sys@";
generate_pathdef_c
函数配置src/pathdef.c.in
,但我们省略了链接标志以简化:
function(generate_pathdef_c) set(_default_vim_dir ${CMAKE_INSTALL_PREFIX}) set(_default_vimruntime_dir ${_default_vim_dir}) set(_all_cflags "${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS}") if(CMAKE_BUILD_TYPE STREQUAL "Release") set(_all_cflags "${_all_cflags} ${CMAKE_C_FLAGS_RELEASE}") else() set(_all_cflags "${_all_cflags} ${CMAKE_C_FLAGS_DEBUG}") endif() # it would require a bit more work and execute commands at build time # to get the link line into the binary set(_all_lflags "undefined") if(WIN32) set(_compiled_user $ENV{USERNAME}) else() set(_compiled_user $ENV{USER}) endif() cmake_host_system_information(RESULT _compiled_sys QUERY HOSTNAME) configure_file( ${CMAKE_CURRENT_LIST_DIR}/pathdef.c.in ${CMAKE_CURRENT_BINARY_DIR}/auto/pathdef.c @ONLY ) endfunction()
在配置时执行 shell 脚本
最后,我们使用以下函数生成osdef.h
:
function(generate_osdef_h) find_program(BASH_EXECUTABLE bash) execute_process( COMMAND ${BASH_EXECUTABLE} osdef.sh ${CMAKE_CURRENT_BINARY_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} ) endfunction()
为了在 ${CMAKE_CURRENT_BINARY_DIR}/src/auto
而不是 src/auto
中生成 osdef.h
,我们不得不修改 osdef.sh
以接受 ${CMAKE_CURRENT_BINARY_DIR}
作为命令行参数。
在osdef.sh
内部,我们检查是否给出了这个参数:
if [ $# -eq 0 ] then # there are no arguments # assume the target directory is current directory target_directory=$PWD else # target directory is provided as argument target_directory=$1 fi
然后,我们生成 ${target_directory}/auto/osdef.h
。为此,我们还需要调整osdef.sh
内部的下述编译行:
$CC -I. -I$srcdir -I${target_directory} -E osdef0.c >osdef0.cc
CMake 秘籍(八)(4)https://developer.aliyun.com/article/1525077