Linux 和 Windows 上安装 CMake

CMake 的安装和简单使用

Posted by Jerry Chen on June 7, 2020

获取软件

官网下载

该页面有源代码和编译好的二进制文件,我们均下载二进制版本;

Linux 上安装

也可使用 sudo apt-get install cmake 的方式安装,如果版本大于 3.10 就可用,否则就需要从官网下载最新版本;

卸载旧版本

如果系统有预装 CMake 就先卸载它;

1
sudo apt-get remove cmake

下载 CMake 并解压

/opt 目录存放可选的程序,比如 firefox 测试版安装到 /opt/firefox_beta;/usr 目录让软件包管理工具(apt)管理;/usr/local 目录存放手动安装的软件,也可放自定义的脚本;

1
2
3
4
5
6
7
sudo mkdir -p /opt
cd /opt

# 下载
sudo wget https://github.com/Kitware/CMake/releases/download/v3.17.3/cmake-3.17.3-Linux-x86_64.tar.gz
# 解压
sudo tar -vxf cmake-3.17.3-Linux-x86_64.tar.gz

解压完成后如图:

创建软链接

1
2
sudo mv /opt/cmake-3.17.3-Linux-x86_64 /opt/cmake-3.17.3
sudo ln -sf /opt/cmake-3.17.3/bin/* /usr/bin/

完成后查看版本:

1
cmake --version

如果你的系统支持 GUI,你可以输入 cmake-gui 进行启动;

Windows 上安装

安装前也要卸载以前装的旧版本;

下载安装包

点这下载 cmake-3.17.3-win64-x64.msi

进行安装

选择添加环境变量:

选择安装路径:

打开这样的 GUI 界面就安装成功了;

CMake 简单使用

单个 main.c

编写文件

项目结构:

编写:main.cpp

1
2
3
4
5
6
7
#include <iostream>
using namespace std;

int main(int argc, char *argv[]){
    cout << "hello cmake!" << endl;
    return 0;
}

编写:CMakeLists.txt

project 设置工程名;

add_executable 设置目标二进制文件,格式:二进制名 cxx文件1 [cxx文件2] [...]

1
2
3
4
5
6
7
cmake_minimum_required(VERSION 3.10)

# set the project name
project(CMakeDemo)

# add the executable
add_executable(main main.cpp)
使用 cmake

使用默认编译工具链 gcc 和 g++,后面会提到如何指定编译器;

建立 build 文件夹,并使用 cmake 生成 Makefile:

1
2
3
mkdir build
cd build
cmake ..
进行 make

没有报错就可继续编译:

1
make

编译后 build 文件夹下会生成二进制文件 main,执行它试试;

单文件夹多文件

编写文件

项目结构:

编写 main.cpp:

1
2
3
4
5
6
#include "simple_print.h"

int main(int argc, char *argv[]){
    simple_print("hello cmake!");
    return 0;
}

编写 simple_print.cpp:

1
2
3
4
5
#include "simple_print.h"

void simple_print(string sStr){
    cout << sStr << endl;
}

编写 simple_print.h:

1
2
3
4
5
6
7
8
9
10
#ifndef __SIMPLE_PRINT_H_
#define __SIMPLE_PRINT_H_

#include <iostream>
#include <string>
using namespace std;

void simple_print(string sStr);

#endif //__SIMPLE_PRINT_H_

编写:CMakeLists.txt

project 设置工程名,执行时会生成一个变量 PROJECT_NAME;

add_executable 设置目标二进制文件,格式:二进制名 cxx文件1 [cxx文件2] [...]

1
2
3
4
5
6
7
cmake_minimum_required(VERSION 3.10)

# set the project name
project(CMakeDemo)

# add the executable
add_executable(main main.cpp simple_print.cpp)
使用 cmake

使用默认编译工具链 gcc 和 g++,后面会提到如何指定编译器;

建立 build 文件夹,并使用 cmake 生成 Makefile:

1
2
3
mkdir build
cd build
cmake ..
进行 make

没有报错就可继续编译:

1
make

编译后 build 文件夹下会生成二进制文件 main,执行它试试;

单文件夹多文件2

编写文件

项目结构:

编写 main.cpp:

1
2
3
4
5
6
7
#include "simple_print.h"
#include "simple_print2.h"

int main(int argc, char *argv[]){
    simple_print("hello cmake!");
    return 0;
}

编写 simple_print.cpp:

1
2
3
4
5
#include "simple_print.h"

void simple_print(string sStr){
    cout << sStr << endl;
}

编写 simple_print.h:

1
2
3
4
5
6
7
8
9
10
#ifndef __SIMPLE_PRINT_H_
#define __SIMPLE_PRINT_H_

#include <iostream>
#include <string>
using namespace std;

void simple_print(string sStr);

#endif //__SIMPLE_PRINT_H_

编写 simple_print2.cpp:

1
2
3
4
5
#include "simple_print2.h"

void simple_print2(void){
    cout << "test test test..." << endl;
}

编写 simple_print2.h:

1
2
3
4
5
6
7
8
9
#ifndef __SIMPLE_PRINT2_H_
#define __SIMPLE_PRINT2_H_

#include <iostream>
using namespace std;

void simple_print2(void);

#endif //__SIMPLE_PRINT2_H_

编写:CMakeLists.txt

add_library 可选链接库类型:STATIC,SHARED 或 MODULE;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 设置 CMake 最低可用版本
cmake_minimum_required(VERSION 3.10)

# 设置工程名称和其他属性
project(CMakeDemo   VERSION 1.0
                    DESCRIPTION "我的 CMake Demo"
                    LANGUAGES CXX)

# 生成动态链接库 simple_print.so
add_library(simple_print SHARED simple_print.cpp simple_print.h)
# 指定标准为 c++ 11
target_compile_features(simple_print PUBLIC cxx_std_11)

# 生成动态链接库 simple_print2.so
add_library(simple_print2 SHARED simple_print2.cpp simple_print2.h)
# 指定标准为 c++ 11
target_compile_features(simple_print2 PUBLIC cxx_std_11)

# 生成可执行文件
add_executable(main main.cpp)
# 添加依赖
target_link_libraries(main simple_print simple_print2)
使用 cmake

使用默认编译工具链 gcc 和 g++,后面会提到如何指定编译器;

建立 build 文件夹,并使用 cmake 生成 Makefile:

1
2
3
mkdir build
cd build
cmake ..
进行 make

没有报错就可继续编译:

1
make

编译后 build 文件夹下会生成 libsimple_print.alibsimple_print2.a 和二进制文件 main,执行它试试;

多文件夹多文件

编写文件

项目结构:

app_version.h 是由 app_version.h.in 进行 cmake 后生成的;

编写 main.cpp:

1
2
3
4
5
6
7
8
9
10
#include "app_version.h"
#include "simple_print.h"
#include "simple_print2.h"

int main(int argc, char *argv[]){
    simple_print("hello cmake!");
    simple_print("project version is " MY_VERSION);
    simple_print2();
    return 0;
}

编写 simple_print.cpp:

1
2
3
4
5
#include "simple_print.h"

void simple_print(string sStr){
    cout << sStr << endl;
}

编写 simple_print.h:

1
2
3
4
5
6
7
8
9
10
#ifndef __SIMPLE_PRINT_H_
#define __SIMPLE_PRINT_H_

#include <iostream>
#include <string>
using namespace std;

void simple_print(string sStr);

#endif //__SIMPLE_PRINT_H_

编写 simple_print2.cpp:

1
2
3
4
5
#include "simple_print2.h"

void simple_print2(void){
    cout << "test test test..." << endl;
}

编写 simple_print2.h:

1
2
3
4
5
6
7
8
9
#ifndef __SIMPLE_PRINT2_H_
#define __SIMPLE_PRINT2_H_

#include <iostream>
using namespace std;

void simple_print2(void);

#endif //__SIMPLE_PRINT2_H_

编写 app_version.h.in

1
2
3
4
5
6
#ifndef __APP_VERSION_H_
#define __APP_VERSION_H_

#define MY_VERSION "@PROJECT_VERSION@"

#endif //__APP_VERSION_H_

编写:CMakeLists.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 设置 CMake 最低可用版本
cmake_minimum_required(VERSION 3.10)

# 设置工程名称和其他属性
project(CMakeDemo   VERSION 1.0
                    DESCRIPTION "我的 CMake Demo"
                    LANGUAGES CXX)

# 由 app_version.h.in 生成 app_version.h
configure_file (
    "${PROJECT_SOURCE_DIR}/version/app_version.h.in"
    "${PROJECT_SOURCE_DIR}/version/app_version.h"
)

# 生成静态链接库 simple_print.a
add_library(simple_print STATIC
            simple_print/simple_print.cpp
            simple_print/simple_print.h)
# 指定头文件夹
target_include_directories(simple_print PUBLIC simple_print)
# 指定标准为 c++ 11
target_compile_features(simple_print PUBLIC cxx_std_11)

# 生成静态链接库 simple_print2.a
add_library(simple_print2 STATIC
            simple_print/simple_print2.cpp
            simple_print/simple_print2.h)
# 指定头文件夹
target_include_directories(simple_print2 PUBLIC simple_print2)
# 指定标准为 c++ 11
target_compile_features(simple_print2 PUBLIC cxx_std_11)

# 生成可执行文件
add_executable(main main/main.cpp)
target_include_directories(main PUBLIC version)
# 添加依赖
target_link_libraries(main simple_print simple_print2)
使用 cmake

使用默认编译工具链 gcc 和 g++,后面会提到如何指定编译器;

建立 build 文件夹,并使用 cmake 生成 Makefile:

1
2
3
mkdir build
cd build
cmake ..
进行 make

没有报错就可继续编译:

1
make

编译后 build 文件夹下会生成 libsimple_print.alibsimple_print2.a 和二进制文件 main,执行它试试;

CMake 常用指令和模板

模式

debug 模式(默认),会包含调试信息,且不做任何优化:

1
2
3
4
mkdir Debug
cd Debug 
cmake -DCMAKE_BUILD_TYPE=Debug .. 
make

release 模式,进行优化:

1
2
3
4
mkdir Release
cd Release 
cmake -DCMAKE_BUILD_TYPE=Release .. 
make

一般工程里只放一个 CMakeList;当然,每个模块作为单独项目也有一个 CMakeList,可通过总工程用 add_subdirectory(your_subdir_target) 包含模块;

如果编写的是工程就把head和src的文件遍历写入 add_execueable 、add_library,设置include_directory等,基本就可以生成了。

如果编的是个库,那可以新建 test 目录下再写一个 cmakelist,用 taget_library_link 和 add_directory(子模块的 target)就可以。

模板一

注意,这个 CMakeList 进行 make 可能报错,仅供参考;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 指定最小 CMake 版本
cmake_minimum_required(VERSION 3.10)

# 项目名称
project(lvgl)

# 设置变量 C 标准为 11
set(CMAKE_C_STANDARD 11)#C11

# 设置变量 C++ 标准为 17
set(CMAKE_CXX_STANDARD 17)#C17

# 设置变量 打开 C++ 依赖
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 全局包含目录,这里包含程序目录
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR})

# 查找满足规则的文件名放到 INCLUDES 变量
file(GLOB_RECURSE INCLUDES "lv_drivers/*.h" "lv_examples/*.h"  "lvgl/*.h"  "./*.h" )

# 查找满足规则的文件名放到 SOURCES 变量
file(GLOB_RECURSE SOURCES  "lv_drivers/*.c" "lv_examples/*.c"  "lvgl/*.c" )

# 设置二进制文件路径
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 

# 找库,命名靶为 SDL2
find_package(SDL2 REQUIRED SDL2)

# 全局包含文件夹,这个变量 SDL2_INCLUDE_DIRS 可能是找到的库中声明好的
include_directories(${SDL2_INCLUDE_DIRS})

# 生成二进制文件
add_executable(main main.c mouse_cursor_icon.c ${SOURCES} ${INCLUDES})

# 给靶 main 链接 SDL2 库
target_link_libraries(main PRIVATE SDL2 )

# 增加一个没有输出的目标 run
# 这样生成 Makefile 后,输入 make run 就可以执行 ${EXECUTABLE_OUTPUT_PATH}/main
add_custom_target (run COMMAND ${EXECUTABLE_OUTPUT_PATH}/main)
模板二
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 指定 CMake 最小版本
cmake_minimum_required(VERSION 3.6)

# 项目名称
project(BoostCoroutineDemo)

# c++标准
set(CMAKE_CXX_STANDARD 11)

# 指定生成的版本
set(CMAKE_BUILD_TYPE "DEBUG")

# 指定编译选项
set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall")

# 设置变量指定源代码
set(SOURCE_FILES main.cpp)

# 指定全局头文件目录
include_directories("/usr/local/boost-1.57/include/")

# 指定全局静态和动态文件目录
link_directories("/usr/local/boost-1.57/lib")

# 生成目标文件
add_executable(BoostCoroutineDemo ${SOURCE_FILES})

# 链接库文件 
target_link_libraries(BoostCoroutineDemo libboost_system.a libboost_thread.a)
target_link_libraries(BoostCoroutineDemo pthread)
模板三

从路径找到文件名(不含后缀)的 CMakeLists.txt;

1
2
3
4
5
6
7
8
9
# 递归找到所有 *.xxx 并赋值到变量 SRC_FILES
FILE(GLOB_RECURSE SRC_FILES "*.c" "*.cc" "*.cpp" "*.h" "*.hpp")
# 遍历 SRC_FILES 的每项赋值到 FILE_PATH
FOREACH(FILE_PATH ${SRC_FILES})
    MESSAGE(${FILE_PATH})
    # 正则匹配到文件名
    STRING(REGEX REPLACE ".+/(.+)\\..*" "\\1" FILE_NAME ${FILE_PATH})
    MESSAGE(${FILE_NAME})
ENDFOREACH(FILE_PATH)
模板四

遍历所有子目录并添加到全局包含目录的函数 include_sub_directories_recursively,其参数 root_dir 需要输入完整路径;

以下是 “多文件夹多文件”的另一个 CMakeLIst 写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 设置 CMake 最低可用版本
cmake_minimum_required(VERSION 3.10)

# 设置工程名称和其他属性
project(CMakeDemo   VERSION 1.0
                    DESCRIPTION "我的 CMake Demo"
                    LANGUAGES CXX)

function(include_sub_directories_recursively root_dir)
    if (IS_DIRECTORY ${root_dir})               # 当前路径是一个目录吗,是的话就加入到包含目录
        #        if (${root_dir} MATCHES "include")
        message("include dir: " ${root_dir})
        include_directories(${root_dir})
        #        endif()
    endif()

    file(GLOB ALL_SUB RELATIVE ${root_dir} ${root_dir}/*) # 获得当前目录下的所有文件,让如ALL_SUB列表中
    foreach(sub ${ALL_SUB})
        if (IS_DIRECTORY ${root_dir}/${sub})
            include_sub_directories_recursively(${root_dir}/${sub}) # 对子目录递归调用,包含
        endif()
    endforeach()
endfunction()

# 由 app_version.h.in 生成 app_version.h
configure_file (
    "${PROJECT_SOURCE_DIR}/version/app_version.h.in"
    "${PROJECT_SOURCE_DIR}/version/app_version.h"
)

# 设置变量 C 标准为 99
set(CMAKE_C_STANDARD 99)
# 设置变量 C++ 标准为 11
set(CMAKE_CXX_STANDARD 11)
# 设置变量 打开 C++ 依赖
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 全局包含目录,这里包含程序目录
include_directories(${PROJECT_SOURCE_DIR})
include_sub_directories_recursively(${PROJECT_SOURCE_DIR}/simple_print)
include_sub_directories_recursively(${PROJECT_SOURCE_DIR}/version)

# 查找满足规则的文件名放到 SOURCES 变量
file(GLOB_RECURSE SOURCES "simple_print/*.cpp")

# 设置二进制文件路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 

# 生成可执行文件
add_executable(main main/main.cpp ${SOURCES})

# 增加一个没有输出的目标 run
# 这样生成 Makefile 后,输入 make run 就可以执行 ${EXECUTABLE_OUTPUT_PATH}/main
add_custom_target (run COMMAND ${EXECUTABLE_OUTPUT_PATH}/main)
模板五
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
cmake_minimum_required(VERSION 3.5)
                           
project(main)         
#MESSAGE(FATAL_ERROR "${CMAKE_BUILD_TYPE}")

if(CMAKE_COMPILER_IS_GNUCC)
    message("COMPILER IS GNUCC")    
    ADD_DEFINITIONS ( -std=c++11 )  
endif(CMAKE_COMPILER_IS_GNUCC)

# 1. protobuf
# 1.1. Find required protobuf package
find_package(Protobuf REQUIRED)
if(PROTOBUF_FOUND)
    message(STATUS "protobuf library found")
else()
    message(FATAL_ERROR "protobuf library is needed but cant be found")
endif()
# 1.2. 生成pb.h、pb.cc必须要加的指令    
include_directories(${PROTOBUF_INCLUDE_DIRS})
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${CMAKE_SOURCE_DIR}/include/protobuf/proto/config.proto)
# 1.3. head file path,头文件目录
message(${PROTO_HDRS})
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include/protobuf)

# 2. CUDA
set(CUDA_DIR "/usr/local/cuda")
find_package(CUDA)
SET(CUDA_NVCC_FLAGS --disable-warnings;-std=c++11;-O3;-gencode arch=compute_75,code=sm_75)
#CUDA_ADD_LIBRARY(${PROJECT_NAME} SHARED)
message(${CUDA_NVCC_FLAGS})

# 3. opencv
#set(OpenCV_DIR "/data/wuh/open_source/opencv-3.4.6/build")
#find_package(OpenCV REQUIRED)
#include_directories(${OpenCV_INCLUDE_DIRS})
link_directories("/usr/local/lib")
include_directories("/usr/local/include/opencv4")
link_directories("")

#message(${SOURCE_FILES})

# 4. tensorRT
include_directories(/data/wuh/software/TensorRT-5.1.2.2/include)
link_directories(/data/wuh/software/TensorRT-5.1.2.2/lib)
link_directories(/data/wuh/project/test_algorithm_module/algorithm_module/build/)
LINK_LIBRARIES(algorithm)


# 5. 头文件
# 5.1. 定义函数,用于递归添加头文件
function(include_sub_directories_recursively root_dir)
    if (IS_DIRECTORY ${root_dir})               # 当前路径是一个目录吗,是的话就加入到包含目录
        message("include dir: " ${root_dir})
        include_directories(${root_dir})
    endif()

    file(GLOB ALL_SUB RELATIVE ${root_dir} ${root_dir}/*) # 获得当前目录下的所有文件,让如ALL_SUB列表中
    foreach(sub ${ALL_SUB})
        if (IS_DIRECTORY ${root_dir}/${sub})
            include_sub_directories_recursively(${root_dir}/${sub}) # 对子目录递归调用,包含
        endif()
    endforeach()
endfunction()
# 5.2. 添加头文件
include_sub_directories_recursively(${CMAKE_SOURCE_DIR}/include) # 对子目录递归调用,包含
MESSAGE(STATUS "CMAK_SOURCE_DIR" ${CMAKE_SOURCE_DIR})

# 6. 添加源文件
FILE(GLOB_RECURSE SOURCE_FILES ${CMAKE_SOURCE_DIR}/src/*.cu  ${CMAKE_SOURCE_DIR}/src/*.cpp)
#message(${SOURCE_FILES})

# 7. 添加链接库
LINK_LIBRARIES(opencv_ml opencv_objdetect opencv_imgproc opencv_core opencv_highgui opencv_imgcodecs opencv_shape opencv_videoio opencv_video)
LINK_LIBRARIES(nvcaffe_parser nvinfer nvinfer_plugin nvparsers)
LINK_LIBRARIES(avcodec avformat swscale avutil)
LINK_LIBRARIES(cuda nvcuvid)
#LINK_LIBRARIES(algorithm)
LINK_LIBRARIES(glog)

# 8.source directory,源文件目录
AUX_SOURCE_DIRECTORY(test DIR_SRCS)

# 9. 设置环境变量,编译用到的源文件全部都要放到这里,否则编译能够通过,
#但是执行的时候会出现各种问题,比如"symbol lookup error xxxxx , undefined symbol"
SET(ALL_SRCS ${DIR_SRCS} ${PROTO_SRCS} ${SOURCE_FILES} ${M_INCLUDE_FILES})

# 10.add executable file,添加要编译的可执行文件
# ADD_EXECUTABLE(${PROJECT_NAME} ${ALL_SRCS})
CUDA_ADD_EXECUTABLE(${PROJECT_NAME} ${ALL_SRCS})

# 11. 链接目标文件与库文件,添加可执行文件所需要的库,比如我们用到了libm.so(命名规则:lib+name+.so),就添加该库的名称
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${PROTOBUF_LIBRARIES})
target_link_libraries(${PROJECT_NAME} /usr/local/cuda-10.1/lib64/libcudart.so)
target_link_libraries(${PROJECT_NAME} /usr/local/cuda-10.1/lib64/libcudnn.so)
模板五
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
cmake_minimum_required (VERSION 3.10)

INCLUDE_DIRECTORIES(../../thirdparty/comm)

FIND_LIBRARY(COMM_LIB comm ../../thirdparty/comm/lib NO_DEFAULT_PATH)
FIND_LIBRARY(RUNTIME_LIB rt /usr/lib  /usr/local/lib NO_DEFAULT_PATH)

link_libraries(${COMM_LIB} ${RUNTIME_LIB})

ADD_DEFINITIONS(
-O3 -g -W -Wall
 -Wunused-variable -Wunused-parameter -Wunused-function -Wunused
 -Wno-deprecated -Woverloaded-virtual -Wwrite-strings
 -D__WUR= -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DTIXML_USE_STL
)

add_library(lib_demo
        cmd.cpp
        global.cpp
        md5.cpp
)

link_libraries(lib_demo)
add_executable(demo
        main.cpp
)

# link library in static mode
target_link_libraries(demo libuuid.a)