cmake入门
1 cmake基础
1.1 简介
CMake 和makefile关系
不同平台有自己的make标准。如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile ,这将是一件让人抓狂的工作。
CMake就是针对上面问题所设计的工具:它首先允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。从而做到“Write once, run everywhere”。
1.2 语法特性介绍
基本语法格式:指令(参数 1 参数 2...)
- 参数使用括弧括起
- 参数之间使用空格或分号分开
指令是大小写无关的,参数和变量是大小写相关的,如:
set(HELLO hello.cpp)
add_executable(hello main.cpp hello.cpp)
ADD_EXECUTABLE(hello main.cpp ${HELLO})
变量使用${}方式取值,但是在 IF 控制语句中是直接使用变量名
1.3 重要指令和CMake常用变量
重要指令:
cmake_minimum_required(VERSION versionNumber [FATAL_ERROR]) 指定CMake的最小版本要求
# CMake最小版本要求为2.8.3
cmake_minimum_required(VERSION 2.8.3)
project(projectname [CXX] [C] [Java]) 定义工程名称,并可指定工程支持的语言。一般只定义工程名称。
# 指定工程名为HELLOWORLD
project(HELLOWORLD)
set(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]]) 显式的定义变量
# 定义SRC变量,其值为sayhello.cpp hello.cpp
set(SRC sayhello.cpp hello.cpp)
include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...) 向工程添加多个特定的头文件搜索路径 --->相当于指定g++编译器的-I参数
# 将/usr/include/myincludefolder 和 ./include 添加到头文件搜索路径
include_directories(/usr/include/myincludefolder ./include)
link_directories(dir1 dir2 ...) 向工程添加多个特定的库文件搜索路径 --->相当于指定g++编译器的-L参数
# 将/usr/lib/mylibfolder 和 ./lib 添加到库文件搜索路径
link_directories(/usr/lib/mylibfolder ./lib)
add_library(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN) 生成库文件
# 通过变量 SRC 生成 libhello.so 共享库,即将SRC中的文件编译成共享库文件
add_library(hello SHARED ${SRC})
add_compile_options()添加编译参数
# 添加编译参数 -Wall -std=c++11 -O2
add_compile_options(-Wall -std=c++11 -O2)
add_executable(exename source1 source2 ... sourceN) 生成可执行文件
# 编译main.cpp生成可执行文件main
add_executable(main main.cpp)
target_link_libraries(target library1<debug | optimized> library2...) 为 target 添加需要链接的共享库 --->相同于指定g++编译器-l参数
# 将hello动态库文件链接到可执行文件main
target_link_libraries(main hello)
# 当前目录中有一个子项目,子项目中有一个CMakeLists.txt文件,用于构建子项目。此时在主项目的CMakeLists.txt文件中就要
# 使用add_subdirectory说明这个子项目的存在,这样在执行主项目的CMakeLists.txt文件的时候,子项目的CMakeLists.txt也会被执行
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
# 添加src子目录,src中需有一个CMakeLists.txt
add_subdirectory(src)
add_subdirectory的具体使用可参考:https://www.jianshu.com/p/07acea4e86a3
aux_source_directory(dir VARIABLE) 发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表
# 定义SRC变量,其值为当前目录下所有的源代码文件
aux_source_directory(. SRC)
# 编译SRC变量所代表的源代码文件,生成main可执行文件
add_executable(main ${SRC})
1.4 CMake编译工程
CMake目录结构:项目主目录存在一个CMakeLists.txt文件
两种方式设置编译规则:
- 包含源文件的子文件夹包含CMakeLists.txt文件,主目录的CMakeLists.txt通过add_subdirectory添加子目录即可;
- 包含源文件的子文件夹未包含CMakeLists.txt文件,子目录编译规则体现在主目录的CMakeLists.txt中;
两种构建方式:
内部构建(in-source build)【不推荐使用】:内部构建会在同级目录下产生一大堆中间文件,这些中间文件并不是我们最终所需要的,和工程源文件放在一起会显得杂乱无章。
## 内部构建
# 在当前目录下,编译本目录的CMakeLists.txt,生成Makefile和其他文件
cmake .
# 执行make命令,生成target
make
外部构建(out-of-source build)【推荐使用】:将编译输出文件与源文件放到不同目录中
## 外部构建
# 1. 在当前目录下,创建build文件夹
mkdir build
# 2. 进入到build文件夹
cd build
# 3. 编译上级目录的CMakeLists.txt,生成Makefile和其他文件
cmake ..
# 4. 执行make命令,生成target
make
2 实例
2.1 内部构建和外部构建
helloworld.cpp:
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
cout << "Hello World!" << endl;
return 0;
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.0)
project(HELLOWORLD)
add_executable(helloWorld_cmake helloworld.cpp) # 等价于g++ helloworld.cpp -o helloWorld_cmake
内部构建(in source build)
cmake
cmake .
-- 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 #检查c和c++语言编译器
-- 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 done
-- Generating done
-- Build files have been written to: /home/ubuntu1/projects/test_c++/test2/Class_5/5.3.1 helloWorld
cmake命令生成了四个个文件:
CMakeCache.txt
CMakeFiles
cmake_install.cmake
# 初学者不需要管上面三个文件
Makefile
make:
make
Scanning dependencies of target helloWorld_cmake
[50%] Building CXX object CMakeFiles/helloWorld_cmake.dir/helloworld.cpp.o # 正在生成.o文件
[100%] Linking CXX executable helloWorld_cmake # 连接
[100%] Built target helloWorld_cmake
make命令生成了可执行文件helloWorld_cmake
外部构建(out of source)【推荐】
mkdir build
cd build
cmake ..
make
外部构建就是将cmake生成的中间文件、Makefile和可执行文件都放在build目录中了。
2.2 动态库链接和加载
实例2的目录结构如下所示:
tree . # tree .命令获取当前目录的结构
.
├── CMakeLists.txt
├── include
│ └── swap.h
├── main.cpp
└── src
└── swap.cpp
main.cpp:
#include "swap.h"
int main(int argc, char **argv)
{
swap myswap(10, 20);
std::cout << "Before swap:" << std::endl;
myswap.printInfo();
myswap.run();
std::cout << "After swap:" << std::endl;
myswap.printInfo();
return 0;
}
swap.h:
#pragma once // 用于防止头文件的重复包含
#include <iostream>
class swap
{
public:
swap(int a, int b){
this->_a = a;
this->_b = b;
}
void run();
void printInfo();
private:
int _a;
int _b;
};
swap.cpp:
#include "swap.h"
void swap::run()
{
int temp;
temp = _a;
_a = _b;
_b = temp;
}
void swap::printInfo()
{
std::cout << "_a = " << _a << std::endl;
std::cout << "_b = " << _b << std::endl;
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.0)
project(SWAP)
include_directories(include) # 等价于-I include
add_library(swap SHARED src/swap.cpp) # 生成动态库
add_executable(main_cmake main.cpp) # 生成可执行文件
target_link_libraries(main_cmake swap) # 链接库文件
编译:
mkdir build
cd build
cmake ..
make
3 CMakeLists.txt文件学习
下面是几个实际项目中的CMakeLists.txt文件,可以看看进行学习,遇到没学过的就去查一查。
3.1 实例1
CMakeLists.txt:
cmake_minimum_required(VERSION 3.0)
project(SOLIDERFIRE) # 项目名为SOLIDERFIRE
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") # -Wall:输出警告的信息
# "${CMAKE_CXX_FLAGS} -Wall"表示在原有的CMAKE_CXX_FLAGS后添加-Wall
# ${CMAKE_CXX_FLAGS}是g++编译选项
set(CMAKE_BUILD_TYPE Debug) # 让输出的可执行文件是可debug的
include_directories(${CMAKE_SOURCE_DIR}/include) # 头文件所在目录
add_executable(my_cmake_exe main.cpp src/Gun.cpp src/Solider.cpp) # 对main.cpp、src/Gun.cpp和src/Solider.cpp三个cpp文件进行编译
3.2 实例2
cmake_minimum_required(VERSION 3.0)
set(SOURCE_FILES main.c)
project(TEST2) # 项目名为TEST2
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") # -Wall:输出警告的信息
# "${CMAKE_CXX_FLAGS} -Wall"表示在原有的CMAKE_CXX_FLAGS后添加-Wall
set(CMAKE_BUILD_TYPE Debug) # 让输出的可执行文件是可debug的
include_directories(${CMAKE_SOURCE_DIR}/ffmpeg/include)
link_directories(
${CMAKE_SOURCE_DIR}/ffmpeg/lib/ # 指明库文件所在目录
)
add_executable(my_cmake_exe avframe.c avpacket.c main.c ) # 对avframe.c avpacket.c main.c 三个cpp文件进行编译
target_link_libraries(my_cmake_exe avcodec avutil m) # 指明具体链接的库的名称
3.3 实例三
可以对下面实例中,不懂的函数或变量进行百度,并进行注释,从而达到学习的目的。【现在懒得学,以后再说吧。】
cmake_minimum_required(VERSION 2.6)
project(pro)
add_definitions(-std=c++11) # CmakeList支持c++11的方式有两种:
# SET(CMAKE_CXX_FLAGS "-std=c++11")
# add_definitions(-std=c++11)
option(CUDA_USE_STATIC_CUDA_RUNTIME OFF)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_BUILD_TYPE Debug)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/workspace)
set(CUDA_GEN_CODE "-gencode=arch=compute_75,code=sm_75")
set(OpenCV_DIR "/data/datav/expstation/lean/opencv4.2.0/lib/cmake/opencv4/")
set(CUDA_DIR "/data/sxai/lean/cuda-10.2")
set(CUDNN_DIR "/data/sxai/lean/cudnn8.2.2.26")
set(TENSORRT_DIR "/data/sxai/lean/TensorRT-8.0.1.6-cuda10.2-cudnn8.2")
find_package(CUDA REQUIRED)
find_package(OpenCV)
include_directories(
${PROJECT_SOURCE_DIR}/src
${OpenCV_INCLUDE_DIRS}
${CUDA_DIR}/include
${TENSORRT_DIR}/include
${CUDNN_DIR}/include
)
link_directories(
${TENSORRT_DIR}/lib
${CUDA_DIR}/lib64
${CUDNN_DIR}/lib
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -O0 -Wfatal-errors -pthread -w -g")
set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS} -std=c++11 -O0 -Xcompiler -fPIC -g -w ${CUDA_GEN_CODE}")
file(GLOB_RECURSE cpp_srcs ${PROJECT_SOURCE_DIR}/src/*.cpp)
file(GLOB_RECURSE c_srcs ${PROJECT_SOURCE_DIR}/src/*.c)
file(GLOB_RECURSE cuda_srcs ${PROJECT_SOURCE_DIR}/src/*.cu)
cuda_add_library(cucodes SHARED ${cuda_srcs})
add_executable(pro ${cpp_srcs} ${c_srcs})
target_link_libraries(cucodes nvinfer nvonnxparser)
target_link_libraries(cucodes cuda cublas cudart cudnn)
target_link_libraries(pro ${OpenCV_LIBS})
target_link_libraries(pro cucodes)
add_custom_target(
run
DEPENDS pro
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/workspace
COMMAND ./pro
)