从mxnet的项目组织结构学习cmake

说明

本系列会介绍MXNet如何使用CMake来组织项目的。

本系列文章涉及大多数实际开放中会用到CMake命令。

本文中的代码来自MXNet

CMake是什么?为什么要学习CMake?

要搞清楚CMake是干什么的,我们需要了解整个C++项目编写的发展过程。由于本人能力有限,这里只是简单介绍一下。

在代码完成编写之后需要通过编译器对代码进行编译,当项目变得庞大之后,每次编译完成之后手动输入编译命令进行编译就成了一件很麻烦的事情,为了解决每次编译需要手动输入编译命令的问题,诞生了Makefile。所谓Makefile就是将整个项目的编译命令写到一个名为Makeifle的文件中,每次只需要输入make就能够进行整个项目的编译,Makefile实现了很多方便的操作,例如一次性查找所有源文件,这样就不用每次增加一个源文件就手动写一条g++ filename.cpp -c -o filename.o命令。

虽然Makefile实现了只需要写一次编译命令,但是当项目变得巨大的时候同时如果需要使用第三方库的时候,Makefile的管理就会稍显麻烦,除了这一点之外,Makefile还有一个更为致命的问题,其不能很好的对跨平台进行支持,也就是如果需要实现两个不同的平台上使用Makefile那么往往需要编写两份Makefile文件。

为了解决上面Makefile遇到的问题,便有了CMakeCMake能够做什么,这里引用官网的一段描述:

CMake is an open-source, cross-platform family of tools designed to build, test and package software. CMake is used to control the software compilation process using simple platform and compiler independent configuration files, and generate native makefiles and workspaces that can be used in the compiler environment of your choice. The suite of CMake tools were created by Kitware in response to the need for a powerful, cross-platform build environment for open-source projects such as ITK and VTK.

上面的英文大致意思就是CMake是一个跨平台的构建工具,用来生成Makefile。这也就是Cmake的主要用途。

CMake现在已经成为很多C++项目使用的构建工具,使用CMake已经成为大势所趋。所以学习CMake就显得尤为重要。

MXNet介绍

MXNet是一个开源的机器学习框架,其支持很多不同的语言,能够方便使用者进行机器学习的训练以及部署。大家熟知的使用Python安装MXNet进行机器学习的训练,而这里的MXNet的代码则是提供一个动态库,对于不同的语言开发者会进行不同的封装,其最终都会调用该动态库。这里MXNet的动态库就是使用C++进行编写的,同时使用CMake进行项目的构建。

MXNet代码组织结构

MXNet的目录下有很多子目录,我们只需要关注其部分目录和文件即可(下面的目录均指仓库根目录下的目录):

src:存放源代码和部分头文件
include:存放对外发布的库的头文件
test:存放测试用例
cmake:存放.cmake文件
3rdparty:存放第三方库
CMakeList.txtCMake的配置文件

如何使用CMake

使用CMake只需要编写CMakeList.txt文件,在完成文件的编写之后,我们可以通过cmake命令(需要保证执行位置下存在CMakeList.txt如果不存在只需要在命令后面输入文件所在路径),该命令便会为我们生成Makefile文件,在生成Makefile文件之后我们只需要执行make命令即可开始构建项目。

MXNet中使用到的CMake命令

接下来我们来学习MXNet是如何使用CMake进行构建的,在上面的代码组织结构中我们发现该项目除了有自己的源代码以外,还使用了第三方库,而这些第三方实际上也是通过CMake来构建的,对于这样的第三方库CMake能够进行很好的管理。

注意:CSDN中不支持 C M a k e CMake CMake代码块,下面的代码块使用 C C C代码块高亮。

cmake_minimum_required(VERSION 3.13)

该命令用于指定cmake的最低版本号,当版本号小于3.13cmake将不会继续执行,而是提示用户升级cmake版本。

project(mxnet C CXX)

指明项目名称和项目使用的语言。

cmake的部分内置变量

一般以CMAKE_开头的变量即为CMake的内置变量,可以按照获取变量值的方法获取各个变量的值,通过set命令可以更改这些变量的值,从而达到一些特殊的目的,一些内置变量:

  • CMAKE_SYSTEM_NAME:目标系统名称,往往需要交叉编译时指定该宏;
  • CMAKE_CROSSCOMPILING:跨平台编译的宏,在指定系统名称后该宏会激活;
  • CMAKE_CXX_STANDARD:使用的C++标准版本;
  • CMAKE_CXX_STANDARD_REQUIRED:是否强制要求C++标准版本,如果开启,当编译器不支持该版本时会报错。如果关闭,当编译器不支持时使用编译器支持的最新版本进行编译;
  • CMAKE_CXX_EXTENSIONS: 是否开启标准版本上的扩展。如果开启那么可以使用编译器支持的一些功能,但是跨平台性可能会被破坏,例如:gcc在此宏开启的情况下会使用-std=gnu++17而不是-std=c++17
  • CMAKE_CXX_COMPILER_ID:编译器的名称,例如GNU编译器该值则为 G N U GNU GNU C l a n g Clang Clang则为 C l a n g Clang Clang
  • CMAKE_CXX_COMPILER_VERSION:编译器的版本号;
  • CMAKE_PROJECT_NAME:顶层项目的名称(如果一个项目包含了一个子项目,那么在子项目中CMAKE_ PROJECT_NAME将会是顶层项目的名称,而不是自己项目的名称);
  • PROJECT_NAME 当前项目的名称,可以通过project()命令进行指定;
  • CMAKE_CURRENT_SOURCE_DIR 当前处理的源目录的绝对路径。

if() elseif() else() endif()

cmake中的逻辑控制语句,在MXNet中的实例如下:

if(CMAKE_CROSSCOMPILING)
  set(__CMAKE_CROSSCOMPILING ${CMAKE_CROSSCOMPILING})
  set(__CMAKE_CROSSCOMPILING_OVERRIDE ON)
endif()

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0)
    message(FATAL_ERROR "MXNet 2 requires a C++17 compatible compiler. Please update to GCC version 7 or newer.")
  endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
    message(FATAL_ERROR "MXNet 2 requires a C++17 compatible compiler. Please update to Clang version 6 or newer.")
  endif()
endif()

if语句中参数为判断的条件。上面出现的STREQUAL用于判断两个字符串是否相等,VERSION_LESS用于判断前一个版本号是否小于后一个版本号。
上述的第一个if语句会在CMAKE_CROSSCOMPILINGON的时候执行,这里的判断语句是用于交叉编译的。

注:CMake中的真假有多种表示方式:True/False, On/Off, TRUE/FALSE, on/off, true/false等。

注:交叉编译多用于实现嵌入式设备的代码编译,由于嵌入式设备性能有限,所以一般性能较强的主机上进行编译,将编译后的目标文件(或库)移植到嵌入式设备即可使用。

补充:

  • AND:逻辑运算符;
  • OR:逻辑运算符;
  • NOT:逻辑运算符;
  • EXISTS:用于判断一个路径(可以是目录也可以是文件)是否存在。

set()

set命令在CMake中可以用于设置变量值,例如:

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS ON)

如果想要获取某个变量则是通过${VARIABLE_NAME}来实现。

message()

用于打印提示信息,类似于日志输出可以指定输出的levelMXNet中使用的几种level如下:

message(FATAL_ERROR "MXNet 2 requires a C++17 compatible compiler. Please update to GCC version 7 or newer.")
message(STATUS "CMAKE_CROSSCOMPILING ${CMAKE_CROSSCOMPILING}")
message(WARNING "Could not find NVML libraries")
message("After choosing blas, linking to ${mxnet_LINKER_LIBS}")

注:日志级别决定了会进行的处理,其中FATAL_ERROE会停止cmake的执行。

注:当不输入级别的时候,代表消息是一个重要消息,用户应该关注。

注:cmake中没有ERROR这个级别,但是MXNet中使用了这个级别,这是不符合逻辑的,此时会将ERROR和日志信息进行拼接。

include()

include可以用于引入宏,模块以及一个文件。

  • 如果引入一个文件(通常后缀为.cmake),此时的用法与C/C++中的include宏用法一致,在mxnet中引入一个文件的用法如下(类似于引入用户自定义头文件):
    include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/Utils.cmake)
    
    上面的命令会引入Utils.cmake,该文件中定义了一些常用的工具函数以及宏。
  • mxnet中还通过include命令引入了模块(类似于引入编译器或者系统头文件),例如:
    include(CMakeDependentOption)
    
    上面引入了CmakeDependentOption该模块中主要支持了cmake_dependent_option方法。

cmake_dependent_option()

该方法用于设置一些宏变量,宏变量在依赖成立的情况下设置为一个值,在依赖不满足的情况下设置为另一个值,例如mxnet中的一个例子:

cmake_dependent_option(USE_NVML "Build with nvml support if found" ON "USE_CUDA" OFF)

USE_NVML会在USE_CUDAON的时候为ON其他的时候为OFF
一个复杂一点的例子:

cmake_dependent_option(A "A will be ON if found" ON "B;NOT C" OFF)

上述的代码表示当B打开,C关闭的时候,A会打开;在其余情况A会关闭。

option()

用于设置一个选项,该选项的变量可以在执行cmake命令的时候通过-DOPTION_NAME=ON/OFF来设置其值。

option(USE_NCCL "Use NVidia NCCL with CUDA" OFF)

例如上面设置了一个USE_NCCL的选项,其默认值为OFF中间的字符串为其的说明。

补充:通过option设置的选项可通过cmake CMakeList.txt -LH(或cmake CMakeList.txt -LA显示所有的)查看。

update 2023.6.23:开坑。
update 2024.1.24::更正错误,添加更详细说明。

find_package()

execute_process()

string()

add_compile_definitions()

add_definitions()

check_language()

enable_language()

find_program()

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值