CMake DSL语言
CMake 快速入门
cmake使用教程
CMake简明教程
CMake 入门实战 | HaHack
Cmake入门和MindsporeLite Cmake文件分析 | 摸黑干活 (fazzie-key.cool)
GitHub - wzpan/cmake-demo: 《CMake入门实战》源码
User Interaction Guide — CMake 3.20.6 Documentation
Home · Wiki · CMake / Community · GitLab (kitware.com)
cmake的一些小经验 - 驭风万里无垠 - C++博客 (cppblog.com)
CMake指令是大小写无关的,即不区分大小写,但建议全部使用大写指令。
add_executable(hello main.cpp hello.cpp)
ADD_EXECUTABLE(hello main.cpp hello.cpp)
变量是大小写相关的,使用 ${}方式取值。但在 if语句 中是直接使用变量名。
set(HELLO hello.cpp) # 设置一个变量HELLO,值是hello.cpp
add_executable(hello main.cpp ${HELLO})
使用 make VERBOSE=1
来查看make构建的详细过程。
-L
链接选项 -L
用于链接时指定显示链接的库的搜索路径(优先级高)。
现代链接器在处理动态库时将**链接时路径(Link-time path)和运行时路径(Run-time path)**分开,用户可以通过-L指定链接时库的路径,通过-R(或-rpath)指定程序运行时库的路径,大大提高了库应用的灵活性。
-R
指定运行时路径有几种方法:拷贝 liba.so
和 libb.so
到系统路径下,修改环境变量 LD_LIBRARY_PATH
或增加配置文件ld.so.confg
中搜索路径。这些都可能改变当前的运行环境,可能导致一些意想不到的事情发生,因此不是很好的方法。对系统影响最小的方法是,在编译的时候指定运行时动态加载库的搜索路径。与之先关的概念有:-R/-rpath/-rpath-link
、rpath/runpath
。
rpath,通过 -Wl,-rpath,d1:..:dn
指定。指定的rpath被 硬编码(hard coding/hard compile) 到二进制文件中,规定了二进制文件动态加载其他库时的最优先顺序。相比最粗粒度的路径控制LD_LIBRARY_PATH
,rpath可以认为是最细粒度的控制。
通过 readelf -d liba.so | grep path
或readelf -d liba.so
可以发现如下一行,表示二进制的动态加载库最优先搜索路径被写入了该二进制文件中:
0x000000000000001d (RUNPATH) Library runpath: [/home/nrsl/workspace/clion/experience/rpath/lib]
链接选项-rpath-link
用于在链接时指定间接依赖库的目录(-link的意思为路径并不写入二进制文件因此不做运行时搜索路径使用)。
当一个共享库1依赖另一个共享库2。则链接库1间接依赖库2。当链接器在执行静态链接时遇到这样的依赖项时,它将自动尝试定位所需的共享库并将其包括在链接中(如果它没有显式包含)。在这种情况下,-rpath-link
选项指定要搜索的第一组目录。-rpath-link
选项可以通过指定用冒号分隔的名称列表或多次出现来指定目录名称序列。
当应用程序直接引用一个十分复杂的库时,这个十分复杂的库背后又依赖了很多间接引用库,使用者没必要知道和花时间研究都有什么间接引用库,这时使用-rpath-link就合适了,让所有直接和间接依赖库都和预期的目标运行环境一致。
因此,rpath-link
用于指定间接依赖的动态库的搜索路径,而-L
为直接依赖的搜索路径。与rpath
的不同在于只在链接期间使用而不在运行期间使用,并且覆盖硬编码到二进制中的rpath
。
-rpath-link
选项应谨慎使用,因为它会覆盖可能已硬编译到共享库中的搜索路径(rpath,runpath)。可能无意中使用与运行时链接器不同的搜索路径。
-rpath-link
与运行时无关!与运行时无关!与运行时无关!
新版本的ld可能默认设置了链接选项 -enable-new-dtags
将-rpath
添加到RUNPATH
。我们可以设置链接选项-disable-new-dtags
来将路径添加到RPATH
。
新特性中RUNPATH
会让RPATH
失效,且优先级低于RPATH
和LD_LIBRARY_PATH
环境变量。虽然RPATH
优先级高,但是当有RUNPATH
的时候RPATH
会被掉过(SKIP)。
-L
用于链接时指定显示链接的库的搜索路径(优先级高)-rpath
用于在链接时指定直接或间接链接的库搜索路径(最高优先级),并且(写入二进制文件中RPATH
)指定运行时的本二进制文件的直接或间接依赖的动态加载库搜索路径(最高优先级)。注:有些系统默认开启链接选项-enable-new-dtags
,导致-rpath
生成RUNPATH
。通过指定链接选项-disable-new-dtags
来使其生成RPATH
。-rpath-link
用于在链接时指定直接或间接链接的库搜索路径(优先级高)。LD_LIBRARY_PATH
在运行时搜索直接或间接依赖。优先级低于RPATH
为第二优先级RUNPATH
写入在二进制文件中,用于指定运行时本二进制文件的直接依赖动态加载库搜索路径(优先级低于LD_LIBRARY_PATH
)。存在时覆盖二进制文件中RPATH
。一文搞懂动态链接库的各种路径的意义与设置
Linux 下的共享库就是普通的 ELF 共享对象。
libname.so.x.y.z
大部分包括 Linux 在内的开源系统遵循 FHS(File Hierarchy Standard)的标准,这标准规定了系统文件如何存放,包括各个目录结构、组织和作用。
/lib
:存放系统最关键和最基础的共享库,如动态链接器、C 语言运行库、数学库等;/usr/lib
:存放非系统运行时所需要的关键性的库,主要是开发库;/usr/local/lib
:存放跟操作系统本身并不十分相关的库,主要是一些第三方应用程序的库。动态链接器会在 /lib
、/usr/lib
和由 /etc/ld.so.conf
配置文件指定的,目录中查找共享库。
LD_LIBRARY_PATH
:临时改变某个应用程序的共享库查找路径,而不会影响其他应用程序;LD_PRELOAD
:指定预先装载的一些共享库甚至是目标文件;LD_DEBUG
:打开动态链接器的调试功能。创建一个名为 MySharedLib 的共享库。
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(MySharedLib)
set(CMAKE_CXX_STANDARD 11)
add_library(MySharedLib SHARED library.cpp library.h)
library.h
#ifndef MYSHAREDLIB_LIBRARY_H
#define MYSHAREDLIB_LIBRARY_H
// 打印 Hello World!
void hello();
// 使用可变模版参数求和
template
T sum(T t)
{
return t;
}
template
T sum(T first, Types ... rest)
{
return first + sum(rest...);
}
#endif
library.cpp
#include
#include "library.h"
void hello() {
std::cout << "Hello, World!" << std::endl;
}
MySharedLib.so
库编译完成后,会生产 MySharedLib.so
库文件。
cd MySharedLib
mkdir build && cd build
cmake ..
make
创建一个名为 TestSharedLib 的可执行文件,调用 MySharedLib.so
共享库。
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(TestSharedLib)
# C++11 编译
set(CMAKE_CXX_STANDARD 11)
# 头文件路径
set(INC_DIR /home/yoyo/MyDocuments/C++Projects/MySharedLib)
# 库文件路径
set(LIB_DIR /home/yoyo/MyDocuments/C++Projects/MySharedLib/build)
include_directories(${INC_DIR})
link_directories(${LIB_DIR})
link_libraries(MySharedLib)
add_executable(TestSharedLib main.cpp)
# 链接 MySharedLib 库
target_link_libraries(TestSharedLib MySharedLib)
main.cpp
#include
#include "library.h"
using std::cout;
using std::endl;
int main() {
hello();
cout << "1 + 2 = " << sum(1,2) << endl;
cout << "1 + 2 + 3 = " << sum(1,2,3) << endl;
return 0;
}
编译完成后,会生产 TestSharedLib
可执行文件。
cd TestSharedLib
mkdir build && cd build
cmake ..
make
执行 TestSharedLib
可执行文件。
Hello, World!
1 + 2 = 3
1 + 2 + 3 = 6
既然我们有了MakeFile,又为什么需要Cmake工具?对于不同环境下的编译,有着多种Make工具,如下所示:
- GNU Make
- QT 的 qmake
- 微软的 MS nmake
- BSD Make(pmake)
- Makepp
这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别。这样就带来了一个严峻的问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile,这将是一件让人抓狂的工作。
CMake 就是针对上面问题所设计的工具:它首先允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。从而做到“Write once, run everywhere”。显然,CMake 是一个比上述几种 make 更高级的编译配置工具。一些使用 CMake 作为项目架构系统的知名开源项目有 VTK、ITK、KDE、OpenCV、OSG 等 [1]。
对于深度学习框架而言,跨平台是一件非常重要的事,因为深度学习模型可能在不同的环境下运行,可能是x86的Linux或者Windows,也可能是ARM,涉及到跨平台交叉编译。
在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:
cmake PATH
或者 ccmake PATH
生成 Makefile(ccmake
和 cmake
的区别在于前者提供了一个交互式的界面)。其中, PATH
是 CMakeLists.txt 所在的目录。make
命令进行编译。单文件CmakeLists,执行命令后会编译一个名为Demo的 exe
文件。
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (mindsporelite)
# 指定生成目标
add_executable(mindsporelite main.cc)
如果把add_executable
改成add_library,那么会生成一个的静态或者动态库。
# 生成动态库
add_library(mindsporelite SHARED main.cc)
# 生成静态库
add_library(mindsporelite STATIC main.cc)
对于多文件的情况,我们可以通过在add_library
或add_executable
的目标后意义列出所有文件,如下:
# 生成动态库
add_library(mindsporelite SHARED main.cc a.cc b.cc)
# 生成静态库
add_library(mindsporelite STATIC main.cc a.cc b.cc)
但是对于一个较大的工程,一一列举会显得Cmake代码十分冗长,我们可以用set
设置一个变量,包含所有需要的.cc
或.cpp
文件:
set(LITE_SRC
${API_SRC}
${CMAKE_CURRENT_SOURCE_DIR}/common/context_util.cc
${CMAKE_CURRENT_SOURCE_DIR}/common/file_utils.cc
${CMAKE_CURRENT_SOURCE_DIR}/common/config_file.cc
${CMAKE_CURRENT_SOURCE_DIR}/common/utils.cc
${CMAKE_CURRENT_SOURCE_DIR}/common/graph_util.cc
${CMAKE_CURRENT_SOURCE_DIR}/common/log.cc
${CMAKE_CURRENT_SOURCE_DIR}/common/lite_utils.cc
${CMAKE_CURRENT_SOURCE_DIR}/common/prim_util.cc
${CMAKE_CURRENT_SOURCE_DIR}/common/tensor_util.cc
${CMAKE_CURRENT_SOURCE_DIR}/runtime/inner_allocator.cc
${CMAKE_CURRENT_SOURCE_DIR}/runtime/runtime_allocator.cc
${CMAKE_CURRENT_SOURCE_DIR}/runtime/infer_manager.cc
${CMAKE_CURRENT_SOURCE_DIR}/schema_tensor_wrapper.cc
${CMAKE_CURRENT_SOURCE_DIR}/tensor.cc
${CMAKE_CURRENT_SOURCE_DIR}/ms_tensor.cc
${CMAKE_CURRENT_SOURCE_DIR}/executor.cc
${CMAKE_CURRENT_SOURCE_DIR}/inner_context.cc
${CMAKE_CURRENT_SOURCE_DIR}/lite_model.cc
${CMAKE_CURRENT_SOURCE_DIR}/kernel_registry.cc
${CMAKE_CURRENT_SOURCE_DIR}/inner_kernel.cc
${CMAKE_CURRENT_SOURCE_DIR}/lite_kernel.cc
${CMAKE_CURRENT_SOURCE_DIR}/lite_kernel_util.cc
${CMAKE_CURRENT_SOURCE_DIR}/sub_graph_kernel.cc
${CMAKE_CURRENT_SOURCE_DIR}/scheduler.cc
${CMAKE_CURRENT_SOURCE_DIR}/lite_session.cc
${CMAKE_CURRENT_SOURCE_DIR}/errorcode.cc
${CMAKE_CURRENT_SOURCE_DIR}/cpu_info.cc
)
# 生成动态库
add_library(mindsporelite SHARED ${LITE_SRC})
# 生成静态库
add_library(mindsporelite STATIC ${LITE_SRC})
当然除了动态库和静态库,我们还可以选择将代码编译成未链接的.o
中间文件,在add_library
使用OBJECT
参数,使用方法如下:
add_library( OBJECT [
对于生成的中间产物,这些文件并未被链接,所以并不能作为库或执行,我们对这些中间产物还可以进行如add_dependencies
的操作,add_dependencies()
会为顶层目标添加一个依赖关系,可以保证某个目标在其他的目标之前被构建。比如mindsporelite
依赖于flatbuffers(一个谷歌开源的序列化库)生成的fbs
文件。
ms_build_flatbuffers_lite(FBS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/schema/ fbs_src ${CMAKE_BINARY_DIR}/schema "")
# 生成中间文件
add_library(lite_src_mid OBJECT ${LITE_SRC})
# 添加依赖,lite_src_mid 依赖 fbs_src
add_dependencies(lite_src_mid fbs_src)
# 生成动态库
add_library(mindsporelite SHARED $)
# 生成静态库
add_library(mindsporelite STATIC $)
有时我们希望在一个项目中能编译多个独立的库或者可执行文件,比如在mindsporelite
中,我们总共会生成以下可执行文件和动静态库
而对于每个库或者可执行文件,一般在其相关的.cc
文件目录下有一个CmakeLists
文件,在最外侧目录的CmakeLists
中通过add_subdirectory
命令,指明本项目包含一个子目录 ,子目录也包含 CMakeLists.txt 文件,这样子目录下的 CMakeLists.txt 文件和源代码也会被处理。通过多层的CmakeLists
我们一一构建各层的库和依赖。以下代码表示添加一个src
的子目录。
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src)
add_subdirectory
往往和target_link_libraries
一起使用,在我们编译好一个可执行文件或者库时,如果它依赖其他库,我们可以使用target_link_libraries
将其链接其他库,方法如下:
# 生成动态库
add_library(mindsporelite SHARED $)
# 生成静态库
add_library(mindsporelite-static STATIC $)
# 生成可执行文件
add_executable(benchmark main.cc)
# 动态链接,将 benchmark 链接到 mindsporelite
# mindsporelite中的 `benchmark ` 基准测试工具依赖于 `mindsporelite` runtime库
target_link_libraries(benchmark mindsporelite)
#静态链接
target_link_libraries(benchmark mindsporelite-static)
链接又分为动态链接和静态链接,静态链接会将库中所有的代码一起编译到可执行文件中,运行时速度更快,但包的大小更大,动态链接不会将库的代码编译到可执行文件中,文件更小,但在运行时会搜索动态库,运行速度慢。如果在系统目录和环境变量中找不到动态库,那么在运行时会报错,在Linux环境中,可以通过设置环境变量LD_LIBRARY_PATH
指定动态库目录:
export LD_LIBRARY_PATH=/path/to/lib:${LD_LIBRARY_PATH}
这里有在链接库时,Cmake是如何找到对应库的位置的?在Cmake中,我们一般在文件开始添加 include_directories(包含指定目录)或aux_source_directory(包含所有子目录)命令,Cmake会在这些目录下进行搜索。
NNIE模型转换环境搭建
OpenCV - https://opencv.org/
opencv_contrib - github
sudo apt-get install build-essential
sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libtiff-dev libjasper-dev libdc1394-22-dev
tar -xvzf opencv-3.4.0.tar.gz
cd opencv-3.4.0
如果编译过程出现问题,就不编译opencv_contrib即可。
mv …/opencv_contrib-3.4.0 ./
mkdir build
mkdir install
cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/x/x \
–D WITH_VTK=ON \
-D OPENCV_EXTRA_MODULES_PATH=/x/x/opencv_contrib-3.4.0/modules/ \
-D CUDA_NVCC_FLAGS="-std=c++11 --expt-relaxed-constexpr" \
-D WITH_NVCUVID=OFF \
-D BUILD_opencv_cudacodec=OFF \
-D ENABLE_CXX11=YES \
..
参数解释:
OPENCV_EXTRA_MODULES_PATH
,opencv_contrib/modules
的路径;CMAKE_INSTALL_PREFIX
,安装的路径;dynlink_nvcuvid.h
和 nvcuvid.h
,所以要将 BUILD_opencv_cudacodec=OFF
。如果编译 opencv-contrib
需要下载boost之类的可以不编译这个,即去掉OPENCV_EXTRA_MODULES_PATH。# 编译
make -j${nproc}
make check
# 安装
make install
protobuf
NNIE模型转换环境搭建
下载地址
tar -xvf protobuf
cd protobuf
autogen.sh
configure -prefix=/you/want/to/install/
make
make check
make install
vim ~/.bashrc
export LD_LIBRARY_PATH=/PATH/TO/lib:$LD_LIBRARY_PATH
export PATH=/PATH/TO/bin:$PATH
CMake语法—环境变量(Environment Variable)
set(ENV{} [])
参数解释
示例
# 定义环境变量
set(ENV{CMAKE_PATH} "F:/cmake")
# 判断CMAKE_PATH环境变量是否定义
if(DEFINED ENV{CMAKE_PATH})
message("CMAKE_PATH_1: $ENV{CMAKE_PATH}")
else()
message("NOT DEFINED CMAKE_PATH VARIABLES")
endif()
cmake_minimum_required(VERSION 3.18)
# 设置工程名称
set(PROJECT_NAME KAIZEN)
# 设置工程版本号
set(PROJECT_VERSION "1.0.0.10" CACHE STRING "默认版本号")
# 工程定义
project(${PROJECT_NAME}
LANGUAGES CXX C
VERSION ${PROJECT_VERSION}
)
# 打印开始日志
message(STATUS "\n########## BEGIN_TEST_ENV_VARIABLE")
# 判断JAVA_HOME变量是否定义
if(DEFINED ENV{JAVA_HOME})
message("JAVA_HOME: $ENV{JAVA_HOME}")
else()
message("NOT DEFINED JAVA_HOME VARIABLES")
endif()
# 定义环境变量
set(ENV{CMAKE_PATH} "F:/cmake")
# 判断CMAKE_PATH环境变量是否定义
if(DEFINED ENV{CMAKE_PATH})
message("CMAKE_PATH_1: $ENV{CMAKE_PATH}")
else()
message("NOT DEFINED CMAKE_PATH VARIABLES")
endif()
# 定义测试函数,在函数中新定义环境变量
function(test_env_variable)
# 访问环境变量CMAKE_PATH
message("CMAKE_PATH_2: $ENV{CMAKE_PATH}")
# 函数内定义环境变量
set(ENV{CMAKE_FUNC} "F:/cmake/dir")
# 判断CMAKE_FUNC环境变量是否定义
if(DEFINED ENV{CMAKE_FUNC})
message("CMAKE_FUNC_1: $ENV{CMAKE_FUNC}")
else()
message("NOT DEFINED CMAKE_FUNC_1 VARIABLES")
endif()
endfunction()
# 调用函数
test_env_variable()
# 判断CMAKE_FUNC环境变量是否定义
if(DEFINED ENV{CMAKE_FUNC})
message("CMAKE_FUNC_2: $ENV{CMAKE_FUNC}")
else()
message("NOT DEFINED CMAKE_FUNC_2 VARIABLES")
endif()
# 如果没有参数值
set(ENV{CMAKE_FUNC})
# 判断CMAKE_FUNC环境变量是否定义
if(DEFINED ENV{CMAKE_FUNC})
message("CMAKE_FUNC_3: $ENV{CMAKE_FUNC}")
else()
message("NOT DEFINED CMAKE_FUNC_3 VARIABLES")
endif()
# 定义测试宏,在函数中新定义环境变量
macro(test_env_var)
# 访问环境变量CMAKE_PATH
message("CMAKE_PATH_3: $ENV{CMAKE_PATH}")
# 宏内定义环境变量
set(ENV{CMAKE_MACRO} "F:/cmake/macro")
# 判断CMAKE_MACRO环境变量是否定义
if(DEFINED ENV{CMAKE_MACRO})
message("CMAKE_MACRO_1: $ENV{CMAKE_MACRO}")
else()
message("NOT DEFINED CMAKE_MACRO_1 VARIABLES")
endif()
endmacro()
# 调用宏
test_env_var()
# 判断CMAKE_MACRO环境变量是否定义
if(DEFINED ENV{CMAKE_MACRO})
message("CMAKE_MACRO_2: $ENV{CMAKE_MACRO}")
else()
message("NOT DEFINED CMAKE_MACRO_2 VARIABLES")
endif()
# 如果多个参数值
set(ENV{CMAKE_FILE} "F:/cmake/cmake1.txt" "F:/cmake/cmake2.txt")
# 判断CMAKE_FILE环境变量是否定义
if(DEFINED ENV{CMAKE_FILE})
message("CMAKE_FILE: $ENV{CMAKE_FILE}")
else()
message("NOT DEFINED CMAKE_FILE VARIABLES")
endif()
# 打印结束日志
message(STATUS "########## END_TEST_ENV_VARIABLE\n")
输出结果
-- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.17763.
-- The CXX compiler identification is MSVC 19.0.24245.0
-- The C compiler identification is MSVC 19.0.24245.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
--
########## BEGIN_TEST_ENV_VARIABLE
JAVA_HOME: C:\Program Files\Java\jdk1.8.0_201
CMAKE_PATH_1: F:/cmake
CMAKE_PATH_2: F:/cmake
CMAKE_FUNC_1: F:/cmake/dir
CMAKE_FUNC_2: F:/cmake/dir
NOT DEFINED CMAKE_FUNC_3 VARIABLES
CMAKE_PATH_3: F:/cmake
CMAKE_MACRO_1: F:/cmake/macro
CMAKE_MACRO_2: F:/cmake/macro
CMake Warning (dev) at CMakeLists.txt:98 (set):
Only the first value argument is used when setting an environment variable.
Argument 'F:/cmake/cmake2.txt' and later are unused.
This warning is for project developers. Use -Wno-dev to suppress it.
CMAKE_FILE: F:/cmake/cmake1.txt
-- ########## END_TEST_ENV_VARIABLE
-- Configuring done
-- Generating done
-- Build files have been written to: F:/learn_cmake/build
请按任意键继续. . .
CMake语法—缓存变量(Cache Variable)
Normal Variable,普通变量,相当于一个局部变量。在同一个CMake工程中使用,会有作用域限制或区分。
Cache Variable,缓存变量,相当于一个全局变量。在同一个CMake工程中任何地方都可以使用。
set( ... CACHE [FORCE])
参数解释
示例
set(MSLITE_REGISTRY_DEVICE "off" CACHE STRING "Compile Mindspore Lite that supports specific devices, currently supported devices: Hi3516D/Hi3519A/Hi3559A/SD3403")
操作符 | 含义 |
---|---|
DEFINED | ~ E,变量被定义了,真 |
NOT | 非,NOT E1 |
AND | 与,E1 AND E2 |
OR | 或,E1 OR E2 |
EXIST | ~ E,存在 name 的文件或者目录(应该使用绝对路径),真 |
COMMAND | ~ E,存在 command-name 命令、宏或函数且能够被调用,真 |
EQUAL | E1 ~ E2,变量值或者字符串匹配 regex 正则表达式 |
LESS | E1 ~ E2,变量值或者字符串匹配 regex 正则表达式 |
GREATER | E1 ~ E2,变量值或者字符串匹配 regex 正则表达式 |
STRLESS | E1 ~ E2,变量值或者字符串为有效的数字且满足小于的条件 |
STRGREATER | E1 ~ E2,变量值或者字符串为有效的数字且满足大于的条件 |
STREQUAL | E1 ~ E2,变量值或者字符串为有效的数字且满足等于的条件 |
STREQUAL
STREQUAL 用于比较字符串,相同返回 true 。
if(NOT("${X86_64_SIMD}" STREQUAL "sse" OR "${X86_64_SIMD}" STREQUAL "avx" OR "${X86_64_SIMD}" STREQUAL "avx512"))
set(KERNEL_SRC_SSE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/fp32/convolution_im2col_sse_fp32.cc
${CMAKE_CURRENT_SOURCE_DIR}/fp32/matmul_fp32_sse.cc
${CMAKE_CURRENT_SOURCE_DIR}/fp32/convolution_winograd_sse_fp32.cc
)
list(REMOVE_ITEM KERNEL_SRC ${KERNEL_SRC_SSE_FILE})
endif()
.cmake
文件cmake(三十五)Cmake之include指令
CMake中include的使用
.cmake
文件是一个模块(module)文件,可以被 include
到 CMakeLists.txt
中。
当 CMakeLists.txt
包含该 .cmake文件
时,当编译运行时,该 .cmake
里的一些命令就会在该 include包含处
得到加载 执行,在后续能够调用该 .cmake
里的一些宏和函数。
include(${CMAKE_SOURCE_DIR}/cmake/options.cmake)
OPTIONAL
选项如果指定了 OPTIONAL
,即使文件不存在也不会触发error。
# xxxx.cmake不存在也不会触发warning或error
include(xxxx.cmake OPTIONAL)
# xxxx.cmake不存在,会触发error
# CMake Error at test_include.cmake:9 (include):
# include could not find requested file: xxxx.cmake
include(xxxx.cmake)
RESULT_VARIABLE
选项如果给定了 RESULT_VARIABLE
,变量 将被设置为已包含的完整的文件名,如果没有找到且指定了
OPTIONAL
则为 NOTFOUND
。
include(test_project.cmake RESULT_VARIABLE var)
# var: /home/spring/GitHub/Linux_Code_Test/Samples_CMake/messy_usage/test_project.cmake
message("var: ${var}")
# xxxx.cmake不存在
include(xxxx.cmake OPTIONAL RESULT_VARIABLE var)
# var: NOTFOUND
message("var: ${var}")
# xxxx.cmake不存在,触发error
# CMake Error at test_include.cmake:20 (include):
# include could not find requested file: xxxx.cmake
include(xxxx.cmake RESULT_VARIABLE var)
注意
.cmake
不要包含工程之类的信息,例如:add_subdirectory()
、CMAKE_CURRENT_
之类的。.cmake
不要调用 CMakeLists.txt
。.cmake
文件放在cmake目录下,路径的前缀为:CMAKE_CURRENT_SOURCE_DIR/cmake/
,建议为绝对路径。FindPython3 — CMake 3.25.0 Documentation
C++ CMake 使用 Python3
如果 find_package()
找到了python3包,对应的python3会包含以下属性。
名称 | 说明 |
---|---|
Python3_Found | 系统具有 Python3 需要的组件 |
Python3_Interpreter_Found | 系统具有 Python3 解释器 |
Python3_EXECUTABLE | Python3 解释器的路径 |
Python3_INTERPRETER_ID | 解释器名称的唯一标识,可能是 Python 、ActivePython 、Anaconda 、Canopy 、IronPython 之一 |
Python3_STDLIB | 标准平台独立安装的目录。可以通过 distutils.sysconfig.get_python_lib(plat_specific=False, standard_lib=True) 获取信息 |
Python3_STDARCH | 标准平台依赖安装的目录。可以通过 distutils.sysconfig.get_python_lib(plat_specific=True,standard_lib=True) 获取信息 |
Python3_SOABI | 模块的扩展名后缀。可以通过 distutils.sysconfig.get_config_flag('SOABI') 或 distutils.sysconfig.get_config_flag('EXT_SUFFIX') 或 python3-config --extension-suffix 获取信息 |
Python3_Compiler_FOUND | 系统具有 Python3 编译器 |
Python3_COMPILER | Python3 编译器的路径,只有使用 IronPython 时提供 |
Python3_COMPILER_ID | 编译器名称的唯一标识,可能是 IronPython |
Python3_Development_FOUND | 系统具有 Python3 开发环境套件 |
Python3_INCLUDE_DIRS | Python3 include 文件目录 |
Python3_LIBRARIES | Python3 库文件 |
Python3_LIBRARY_DIRS | Python3 库文件路径 |
Python3_RUNTIME_LIBRARY_DIRS | Python3 运行时库文件路径 |
Python3_VERSION | Python3 版本 |
Python3_VERSION_MAJOR | Python3 主版本 |
Python3_VERSION_MINOR | Python3 此版本 |
Python3_VERSION_PATCH | Python3 小版本 |
Python3_NumPy_FOUND | 系统具有 Numpy |
Python3_NumPy_INCLUDE_DIRS | NumPy include 文件目录 |
Python3_NumPy_VERSION | NumPy 版本 |
# 如果使用的是非系统目录下的 Python 可以通过指定 Python3_ROOT_DIR 改变查找路径
set(Python3_ROOT_DIR "/home/liulinjun/miniconda3/envs/mslite")
# 寻找python3
find_package(Python3 COMPONENTS Interpreter Development)
if(Python3_FOUND)
set(PYTHON_INCLUDE_DIRS "${Python3_INCLUDE_DIRS}")
set(PYTHON_LIBRARIES "${Python3_LIBRARIES}")
# 寻找numpy组件
find_package(Python3 COMPONENTS NumPy Development)
# 如果找到numpy组件
if(Python3_NumPy_FOUND)
include_directories(${Python3_INCLUDE_DIRS})
include_directories(${Python3_NumPy_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../core/)
include(${TOP_DIR}/cmake/external_libs/pybind11.cmake)
endif()
else()
find_python_package(py_inc py_lib)
set(PYTHON_INCLUDE_DIRS "${py_inc}")
set(PYTHON_LIBRARIES "${py_lib}")
endif()
# python的include路径
message("PYTHON_INCLUDE_DIRS = ${PYTHON_INCLUDE_DIRS}")
# python的lib路径
message("PYTHON_LIBRARIES = ${PYTHON_LIBRARIES}")
# python版本
message("PYTHON_VERSION = ${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}.${Python3_VERSION_PATCH}")
# 包含头文件
include_directories(${PYTHON_INCLUDE_DIRS})
参数解释
Interpreter
:寻找python3解释器;Compiler
:寻找python3编译器,仅使用 IronPython
时提供;Development
:寻找开发环境套件(包含 include
和 lib
目录);Numpy
:寻找 Numpy 组件;如果没有指定 COMPONENT
,默认使用 Interpreter
。
为了确保所有组件 Interpreter
,Compiler
,Development
,Numpy
的版本一致,需要同时指定所有组件,如下指令:
find_package(Python3 COMPONENTS Interpreter Development)
注意:如果 Interpreter
和 Development
组件都被指定的话,这个模块只会搜索 Cmake
配置的平台架构的解释器。如果仅指定了 Interpreter
组件的话,这个约束不会生效。
cmake_minimum_required(VERSION 3.10)
project(c_matrix C CXX)
# 增加 Python3_INCLUDE_DIRS
add_definitions(-D Python3_INCLUDE_DIRS=/home/liulinjun/miniconda3/envs/mslite/include)
# 增加 Python3_LIBRARIES
add_definitions(-D Python3_LIBRARIES=/home/liulinjun/miniconda3/envs/mslite/lib)
find_package(Python3 COMPONENTS Interpreter Development)
if (Python3_FOUND)
message("Python include directory: " ${Python3_INCLUDE_DIRS})
message("Python version is: " ${Python3_VERSION})
include_directories(${ Python3_INCLUDE_DIRS})
target_link_libraries(main ${ Python3_LIBRARIES})
endif (Python3_FOUND)
-- Found PythonInterp: /home/liulinjun/miniconda3/envs/mslite/bin/python (found suitable version "3.8.13", minimum required is "3.6")
-- Found PythonLibs: /home/liulinjun/miniconda3/envs/mslite/lib/libpython3.8.so
cmake -DPYTHON_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython2.7.so \
-DPYTHON_INCLUDE_DIR=/usr/include/python2.7 \
-DPYTHON_EXECUTABLE=/usr/bin/python2.7 \
..
-- Found PythonInterp: /usr/bin/python2.7 (found version "2.7.17")
-- Found PythonLibs: /usr/lib/x86_64-linux-gnu/libpython2.7.so=
如果想要生成安装包,则需要使用 CPack,它是由 CMake 提供的一个工具,专门用于打包。此时需要在 CMakeLists.txt
中添加以下内容:
# 构建一个 CPack 安装包
include(InstallRequiredSystemLibraries)
set(CPACK_RESOURCE_FILE_LICENSE
"${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set(CPACK_PACKAGE_VERSION_MAJOR "${Demo_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${Demo_VERSION_MINOR}")
include (CPack)
参数解释
include(InstallRequiredSystemLibraries)
:导入 InstallRequiredSystemLibraries
模块。include(CPack)
:导入 CPack 模块。接着执行 cmake
和 make
构建工程,此时再执行 cpack
命令即可生成安装包:
#生成二进制安装包
cpack -C CPackConfig.cmake
#生成源码安装包
cpack -C CPackSourceConfig.cmake
当命令执行成功后,就会在当前目录下生成 .sh
、.tar.gz
、.tar.Z
这三个格式的安装包。
# 启动debug模式
set(CMAKE_BUILD_TYPE "Debug")
# 开启 -g 选项
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
CMake目录结构:项目主目录存在一个CMakeLists.txt文件。
两种方式设置编译规则
在linux平台下使用CMake构建C/C++工程的流程
CMakeLists.txt
。CMakeLists.txt
文件所在目录创建一个 build 文件夹,然后进入目录。(这一步可以省略,但是生成的中间文件不易清理)cmake PATH
或者 ccmake PATH
生成 Makefile(ccmake
和 cmake
的区别在于前者提供了一个交互式的界面)。其中, PATH
是 CMakeLists.txt
所在的目录。make
命令进行编译,使用 make install
进行安装。外部构建(out-of-source build),将编译输出文件和源文件放到不同的目录中。
# 外部构建
# 1. 在当前目录下,创建build文件夹
mkdir build
# 2. 进入到build文件夹
cd build
# 3. 编译上级目录的CMakeLists.txt,生成Makefile和其他文件
cmake ..
# 4. 执行make命令,生成target
make
内部构建(in-source build)会在同级目录下产生一大堆中间文件,这些中间文件和工程源文件放在一起显得杂乱无章。
# 内部构建
# 在当前目录下,编译本目录的CMakeLists.txt,生成Makefile和其他文件
cmake .
# 执行make命令,生成target
make
CMake用法示例
对于任何跨平台的Cpp项目,其Cmake的架构基本大同小异,按层级结构一一编译各个动静态库,最后链接成一个可执行文件或者库。
(有空学习一下,亲自尝试编写cmake代码)
cmake-examples
cmake-demo