双笙子佯谬老师的【公开课】现代CMake高级教程课程笔记
第 8 章:跨平台与编译器
在 CMake 中给 .cpp
定义一个宏
#include <cstdio>
int main()
{
#ifdef MY_MACRO
printf("MY_MACRO defined! value: %d\n", MY_MACRO);
#else
printf("MY_MACRO not defined!\n");
#endif
}
CMakeLists.txt
add_executable(main)
file(GLOB sources CONFIGURE_DEPENDS *.cpp *.h)
target_sources(main PUBLIC ${sources})
target_compile_definitions(main PUBLIC MY_MACRO=233)
编译运行结果:
MY_MACRO defined! value: 233
跨平台
根据不同的操作系统,把宏定义成不同的值:
main.cpp
#include <cstdio>
int main()
{
#ifdef MY_NAME
printf("Hello, %s\n", MY_NAME);
#else
printf("I don't know your name!\n");
#endif
}
CMakeLists.txt
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
target_compile_definitions(main PUBLIC MY_NAME="Bill Gates")
elseif (CMAKE_SYSTEM_NAME MATCHES "Linux")
target_compile_definitions(main PUBLIC MY_NAME="Linus Torvalds")
elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin")
target_compile_definitions(main PUBLIC MY_NAME="Steve Jobs")
endif()
编译运行结果:
❯ ./build/main
Hello, Linus Torvalds
CMake 还提供了一些简写变量:WIN32, APPLE, UNIX, ANDROID, IOS 等
CMakeLists.txt
if (WIN32)
target_compile_definitions(main PUBLIC MY_NAME="Bill Gates")
elseif (UNIX AND NOT APPLE)
target_compile_definitions(main PUBLIC MY_NAME="Linus Torvalds")
elseif (APPLE)
target_compile_definitions(main PUBLIC MY_NAME="Steve Jobs")
elseif (ANDROID)
target_compile_definitions(main PUBLIC MY_NAME="Andy Rubin")
elseif (IOS)
target_compile_definitions(main PUBLIC MY_NAME="Steve Jobs")
endif()
编译运行结果:
❯ ./build/main
Hello, Linus Torvalds
使用生成器表达式,简化成一条指令
语法:
$<$<类型:值>:为真时的表达式>
比如
$<$<PLATFORM_ID:Windows>:MY_NAME=”Bill Gates”>
在 Windows 平台上会变为 MY_NAME="Bill Gates"
,其他平台上则表现为空字符串。
target_compile_definitions(main PUBLIC
$<$<PLATFORM_ID:Windows>:MY_NAME="Bill Gates">
$<$<PLATFORM_ID:Linux>:MY_NAME="Linus Torvalds">
$<$<PLATFORM_ID:Darwin>:MY_NAME="Steve Jobs">)
编译运行结果:
❯ ./build/main
Hello, Linus Torvalds
如需多个平台可以用逗号分割:
target_compile_definitions(main PUBLIC
$<$<PLATFORM_ID:Windows>:MY_NAME="Dos-like">
$<$<PLATFORM_ID:Linux,Darwin,FreeBSD>:MY_NAME="Unix-like">)
编译运行结果:
❯ ./build/main
Hello, Unix-like
多编译器
判断当前用的是哪一款 C++ 编译器
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
target_compile_definitions(main PUBLIC MY_NAME="gcc")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "NVIDIA")
target_compile_definitions(main PUBLIC MY_NAME="nvcc")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_definitions(main PUBLIC MY_NAME="clang")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
target_compile_definitions(main PUBLIC MY_NAME="msvc")
endif ()
编译运行结果:
❯ ./build/main
Hello, gcc
也可以用生成器表达式判断编译器:
target_compile_definitions(main PUBLIC
$<$<CXX_COMPILER_ID:GNU,Clang>:MY_NAME="Open-source">
$<$<CXX_COMPILER_ID:MSVC,NVIDIA>:MY_NAME="Commercial">)
编译运行结果:
❯ ./build/main
Hello, Open-source!
生成器表达式也可以做复杂的逻辑判断:
target_compile_definitions(main PUBLIC
$<$<AND:$<CXX_COMPILER_ID:GNU,Clang>,$<PLATFORM_ID:Linux,FreeBSD>>:MY_NAME="Open-source">)
编译运行结果:
❯ ./build/main
Hello, Open-source!
简写变量
CMake 还提供了一些简写变量:MSVC, CMAKE_COMPILER_IS_GNUCC
if (MSVC)
target_compile_definitions(main PUBLIC MY_NAME="MSVC")
elseif (CMAKE_COMPILER_IS_GNUCC)
target_compile_definitions(main PUBLIC MY_NAME="GCC")
else ()
target_compile_definitions(main PUBLIC MY_NAME="Other compiler")
endif ()
编译运行结果:
❯ ./build/main
Hello, GCC
CMAKE_CXX_COMPILER_ID 直接作为字符串变量
target_compile_definitions(main PUBLIC MY_NAME="The ${CMAKE_CXX_COMPILER_ID} Compiler")
编译运行结果:
❯ ./build/main
Hello, The GNU Compiler
从命令行参数指定编译器
cmake -B build -DCMAKE_CXX_COMPILER="/usr/bin/clang++" -DCMAKE_C_COMPILER="/usr/bin/clang"
编译运行结果:
❯ ./build/main
Hello, The Clang Compile
也可以通过环境变量 CXX 指定
$ CXX='which clang' cmake -B build
CMAKE_GENERATOR
message("Generator: ${CMAKE_GENERATOR}")
message("C++ compiler: ${CMAKE_CXX_COMPILER}")
message("C compiler: ${CMAKE_C_COMPILER}")
生成结果:
Generator: Unix Makefiles
C++ compiler: /usr/bin/clang++
C compiler: /usr/bin/clang
vimrc 分享
小彭老师使用的 vimrc 分享:github.com/archibate/vimrc
" for cmake4vim:
let g:cmake_usr_args = '-GNinja'
let g:cmake_build_target = 'main'
let g:cmake_build_type = 'Release'
let g:cmake_compile_commands = 1
let g:cmake_build_path_pattern = ["/tmp/build/%s", "getcmd()"]