一、检测操作系统
CMake是一组跨平台工具。所以需要了解操作系统(OS)上执行配置或构建步骤。从而与操作系统相关的CMake代码,会根据操作系统启用条件编译,或者在可用或必要时使用特定于编译器的扩展。
# set minimum cmake version
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
# project name, in this case no language required
project(recipe-01 LANGUAGES NONE)
# print custom message depending on the operating system
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(STATUS "Configuring on/for Linux")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
message(STATUS "Configuring on/for macOS")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
message(STATUS "Configuring on/for Windows")
elseif(CMAKE_SYSTEM_NAME STREQUAL "AIX")
message(STATUS "Configuring on/for IBM AIX")
else()
message(STATUS "Configuring on/for ${CMAKE_SYSTEM_NAME}")
endif()
CMake为目标操作系统定义了CMAKE_SYSTEM_NAME,因此不需要使用定制命令、工具或脚本来查询此信息。然后,可以使用此变量的值实现特定于操作系统的条件和解决方案。
二、处理与平台相关部分
理想情况下,应该避免依赖于平台的源代码,但是有时我们没有选择,特别是当要求配置和编译不是自己编写的代码时。
# set minimum cmake version
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
# project name and language
project(recipe-02 LANGUAGES C)
# define executable and its source file
add_executable(hello-world hello-world.c)
# let the preprocessor know about the system name
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_compile_definitions(hello-world PUBLIC "IS_LINUX")
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_compile_definitions(hello-world PUBLIC "IS_MACOS")
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
target_compile_definitions(hello-world PUBLIC "IS_WINDOWS")
endif()
些定义在CMakeLists.txt中配置时定义,通过使用target_compile_definition在预处理阶段使用。可以不同的操作系统,使用不同的宏编译。
三、处理与编译器相关的源代码
为了可移植性,我们尽量避免去编写新代码,但遇到有依赖的情况我们也要去解决,特别是当使用历史代码或处理编译器依赖工具时。
# set minimum cmake version
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
# project name and language
project(recipe-03 LANGUAGES C)
# define executable and its source file
add_executable(hello-world hello-world.c)
target_compile_definitions(hello-world PUBLIC "COMPILER_NAME=\"${CMAKE_C_COMPILER_ID}\"")
# let the preprocessor know about the compiler vendor
if(CMAKE_C_COMPILER_ID MATCHES Intel)
target_compile_definitions(hello-world PUBLIC "IS_INTEL_CXX_COMPILER")
endif()
if(CMAKE_C_COMPILER_ID MATCHES GNU)
target_compile_definitions(hello-world PUBLIC "IS_GNU_CXX_COMPILER")
endif()
if(CMAKE_C_COMPILER_ID MATCHES PGI)
target_compile_definitions(hello-world PUBLIC "IS_PGI_CXX_COMPILER")
endif()
if(CMAKE_C_COMPILER_ID MATCHES XL)
target_compile_definitions(hello-world PUBLIC "IS_XL_CXX_COMPILER")
endif()
# etc ...
这部分与二节相似,根据不同的编译器添加不同的宏编译,这样就可以编译不同的代码。
四、检测处理器体系结构
本节将讨论如何在32位及64位机器上移植代码问题。CMake定义了CMAKE_HOST_SYSTEM_PROCESSOR变量,以包含当前运行的处理器的名称。可以设置为“i386”、“i686”、“x86_64”、“AMD64”等等,当然,这取决于当前的CPU。CMAKE_SIZEOF_VOID_P为void指针的大小。我们可以在CMake配置时进行查询,以便修改目标或目标编译定义。
# set minimum cmake version
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
# project name and language
project(recipe-04 LANGUAGES CXX)
# define executable and its source file
add_executable(arch-dependent arch-dependent.cpp)
# let the preprocessor know about the size of void *
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
target_compile_definitions(arch-dependent PUBLIC "IS_64_BIT_ARCH")
message(STATUS "Target is 64 bits")
else()
target_compile_definitions(arch-dependent PUBLIC "IS_32_BIT_ARCH")
message(STATUS "Target is 32 bits")
endif()
# let the preprocessor know about the host processor architecture
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "i386")
message(STATUS "i386 architecture detected")
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "i686")
message(STATUS "i686 architecture detected")
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64")
message(STATUS "x86_64 architecture detected")
else()
message(STATUS "host processor architecture is unknown")
endif()
target_compile_definitions(arch-dependent
PUBLIC "ARCHITECTURE=${CMAKE_HOST_SYSTEM_PROCESSOR}"
)
五、检测处理器指令集
检测到的主机系统信息,可用于设置相应的编译器标志,或实现可选的源代码编译,或根据主机系统生成源代码,我们将使用CMake为平台填充config.in,然后由config.in生成config.h,源代码通过包含config.h来实现不同处理器指令集下的编译。
# set minimum cmake version
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
# project name and language
project(recipe-05 LANGUAGES CXX)
# define executable
add_executable(processor-info "")
# and its source file
target_sources(processor-info
PRIVATE
processor-info.cpp
)
# and its include directories
target_include_directories(processor-info
PRIVATE
${PROJECT_BINARY_DIR}
)
foreach(key
IN ITEMS
NUMBER_OF_LOGICAL_CORES
NUMBER_OF_PHYSICAL_CORES
TOTAL_VIRTUAL_MEMORY
AVAILABLE_VIRTUAL_MEMORY
TOTAL_PHYSICAL_MEMORY
AVAILABLE_PHYSICAL_MEMORY
IS_64BIT
HAS_FPU
HAS_MMX
HAS_MMX_PLUS
HAS_SSE
HAS_SSE2
HAS_SSE_FP
HAS_SSE_MMX
HAS_AMD_3DNOW
HAS_AMD_3DNOW_PLUS
HAS_IA64
OS_NAME
OS_RELEASE
OS_VERSION
OS_PLATFORM
)
cmake_host_system_information(RESULT _${key} QUERY ${key})
endforeach()
configure_file(config.h.in config.h @ONLY)
1.首先定义目标可执行文件及其源文件,并包括目录::
dd_executable(processor-info "")
target_sources(processor-info
PRIVATE
processor-info.cpp
)
target_include_directories(processor-info
PRIVATE
${PROJECT_BINARY_DIR}
)
2.继续查询主机系统的信息,获取一些关键字::
foreach(key
IN ITEMS
NUMBER_OF_LOGICAL_CORES
NUMBER_OF_PHYSICAL_CORES
TOTAL_VIRTUAL_MEMORY
AVAILABLE_VIRTUAL_MEMORY
TOTAL_PHYSICAL_MEMORY
AVAILABLE_PHYSICAL_MEMORY
IS_64BIT
HAS_FPU
HAS_MMX
HAS_MMX_PLUS
HAS_SSE
HAS_SSE2
HAS_SSE_FP
HAS_SSE_MMX
HAS_AMD_3DNOW
HAS_AMD_3DNOW_PLUS
HAS_IA64
OS_NAME
OS_RELEASE
OS_VERSION
OS_PLATFORM
)
cmake_host_system_information(RESULT _${key} QUERY ${key})
endforeach()
3.定义了相应的变量后,配置config.h:
configure_file(config.h.in config.h @ONLY)
foreach循环会查询多个键值,并定义相应的变量。此示例的核心函数是cmake_host_system_information,它查询运行CMake的主机系统的系统信息。每个键使用了一个函数调用。然后,使用这些变量来配置config.h.in中的占位符,输入并生成config.h。