CMake:检测环境

本文介绍了如何使用CMake检测目标平台的操作系统,如Windows、Linux或macOS,并根据平台编写条件编译代码。同时,展示了如何检测编译器相关源代码,处理处理器体系结构,以及检测处理器支持的指令集。通过示例代码,解释了CMake在构建过程中的配置和自定义命令,以确保跨平台兼容性。

导言

通过前面几篇的学习,我们掌握CMake以及与C++相关的基本知识。尽管CMake跨平台,但有时源代码并不是完全可移植。为了使得我们的源代码能够跨平台、操作系统和编译器,根据平台不同的方式配置和/或构建代码是在构建项目过程中必不可少的环节。

检测操作系统

CMake是一组跨平台工具。在实际的开发过程中,我们需要操作系统相关的CMake代码,会根据操作系统启用条件编译,或者在可用或必要时使用特定于编译器的扩展。

这里举个特定示例说明:

WindowsUnix系统的文件结构有明显的差异,如将深度学习模型集成于C++开发的软件系统中时,我们想要将深度学习模型(*.pth)拷贝到指定的文件中时:

if (MSVC)
  file(GLOB MODEL "${CMAKE_SOURCE_DIR}/resource/*.pt")
  add_custom_command(TARGET ${
   
   PROJECT_NAME} POST_BUILD
                     COMMAND ${
   
   CMAKE_COMMAND} -E copy_if_different
                     ${
   
   MODEL}
                     $<TARGET_FILE_DIR:${
   
   PROJECT_NAME}>)
elseif(UNIX)
  file(GLOB MODEL "${CMAKE_SOURCE_DIR}/resource/*.pt")
  file(COPY ${
   
   MODEL} DESTINATION ${
   
   EXECUTE_FILE})
endif()

这段CMake代码用于在构建项目后,根据目标平台的不同(WindowsUNIX/Linux),将模型文件复制到输出目录中,以确保运行程序时所需的模型文件(具有.pt扩展名)与可执行文件位于同一目录下。

对于MSVCMicrosoft Visual C++编译器,通常用于Windows平台):

  • 使用file()命令并设置GLOB选项,在CMake源代码目录(${CMAKE_SOURCE_DIR})下的resource目录中查找所有.pt模型文件。
  • 然后使用add_custom_command()命令将自定义的后期构建命令添加到目标``(${PROJECT_NAME})中。
  • 自定义命令会将找到的所有.pt模型文件复制到输出目录($<TARGET_FILE_DIR:${PROJECT_NAME}>)。使用copy_if_different参数确保仅在目标文件与源文件不同或目标目录中不存在时才复制文件。

对于UNIX平台(包括Linux):

  • 使用file()命令并设置GLOB选项,在CMake源代码目录(\${CMAKE_SOURCE_DIR})下的resource目录中查找所有.pt模型文件。
  • 然后使用file()命令并设置COPY选项,将找到的所有.pt模型文件复制到指定的目标目录(${EXECUTE_FILE})。

接下来,我们将通过一个不需要编译任何源代码的示例,演示如何使用CMake检测操作系统。

项目地址:

https://gitee.com/jiangli01/tutorials/tree/master/cmake-tutorial/chapter2/01

CMakeLists.txt

cmake_minimum_required(VERSION 3.10 FATAL_ERROR)

project(os_test)

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()

输出结果

-- The C compiler identification is GNU 9.4.0
-- The CXX compiler identification is GNU 9.4.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
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring on/for Linux
-- Configuring done
-- Generating done
-- Build files have been written to: /home/jiangli/repo/tutorials/cmake-tutorial/chapter2/01/build

CMake为目标操作系统定义了CMAKE_SYSTEM_NAME,因此不需要使用定制命令、工具或脚本来查询此信息。然后,可以使用此变量的值实现特定于操作系统的条件和解决方案。

  • macOSCMAKE_SYSTEM_NAMEDarwin
  • Linux和Windows上,CMAKE_SYSTEM_NAME分别为LinuxWindows。我们了解了如何在特定的操作系统上执行特定的CMake代码。当然,应该尽量减少这种定制化行为,以便简化迁移到新平台的过程。

注意:为了最小化从一个平台转移到另一个平台时的成本,应该避免直接使用Shell命令,还应该避免显式的路径分隔符(LinuxmacOS上的前斜杠和Windows上的后斜杠)。CMake代码中只使用前斜杠作为路径分隔符,CMake将自动将它们转换为所涉及的操作系统环境。

处理与编译器相关的源代码

为了可移植性,我们尽量避免去编写新代码,但遇到有依赖的情况我们也要去解决,特别是当使用历史代码或处理编译器依赖工具。

项目地址:

https://gitee.com/jiangli01/tutorials/tree/master/cmake-tutorial/chapter2/02

CMakeLists.txt

cmake_minimum_required(VERSION 3.10 FATAL_ERROR)

project(hello_os LANGUAGES CXX)

add_executable(${PROJECT_NAME} hello_os.cpp)

if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
  target_compile_definitions(${PROJECT_NAME} PUBLIC "IS_LINUX")
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
  target_compile_definitions(${PROJECT_NAME} PUBLIC "IS_MACOS")
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
  target_compile_definitions(${PROJECT_NAME} PUBLIC "IS_WINDOWS")
endif()

通过target_compile_definitions()命令向目标(hello_os)添加预定义宏IS_LINUXIS_MACOS或者IS_WINDOWS,该宏在编译过程中将生效。

target_compile_definitions会将将定义限制于一个特定的目标,以及通过PRIVATE|PUBLIC|INTERFACE限定符,限制这些定义可见性:

  • PRIVATE,编译定义将只应用于给定的目标,而不应用于相关的其他目标。
  • INTERFACE,对给定目标的编译定义将只应用于使用它的目标。
  • PUBLIC,编译定义将应用于给定的目标和使用它的所有其他目标。

当然,在C++中,可以直接使用预定义的宏来识别不同的平台和操作系统。这些预定义的宏是由编译器或操作系统提供的,可以在源代码中使用它们来编写平台相关的代码。以下是一些常用的平台识别宏:

  • __APPLE__:在苹果(Apple)平台(例如 macOSiOS)上定义。
  • __linux__:在Linux平台上定义。
  • _WIN32:在32Windows操作系统上定义。
  • _WIN64:在64Windows操作系统上定义。
  • _MSC_VER:在使用Microsoft Visual C++编译器时定义,表示编译器的版本号。
  • __GNUC__:在使用GNU编译器(例如g++)时定义,表示编译器的版本号。

hello_os.cpp

#include <string>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值