C++之CMake语法特性介绍 及 CMake实战演练

Preface

CMake语法特性介绍

  • 基本语法格式:指令(参数1 参数2…)

    • 参数使用括号括起
    • 参数之间使用空格分号分开
  • 指令是大小写无关的,参数和变量是大小写相关的

    set(Hello hello.cpp)
    add_executable(hello main.cpp hello.cpp)
    ADD_EXECUTABLE(hello main.cpp ${HELLO})
    
  • 变量使用${}方式取址,但是在IF控制语句中是直接使用变量名

1. 重要的指令和CMAKE常用变量

1.1 重要指令
  • cmake_minimum_required -指定CMAKE的最小版本要求
#CMake最小版本要求
cmake_minimum_required(VERSION 2.8.3)
语法:cmake_minimum_required(VERSION  versionNumber  [FATAL_ERROR])
  • project - 定义工程名称,并可指定工程支持语言
# 指定工程名为HELLOWORLD
project(HELLOWORLD)
语法:project(projectname [CXX] [C] [Java])
  • set - 显式定义变量
# 定义SRC变量,其值为main.cpp hello.cpp
set(SRC sayhello.cpp hello.cpp)
语法:set(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
  • include_directories - 向工程添加特定的头文件搜索路径 —>相当于指定g++编译器的 -I参数
  # 将/usr/include/myincludefolder 和 ./include 添加到头文件搜索路径
  include_directories(/usr/include/myincludefolder ./include)
语法:include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 …)
  • link_directories - 向工程添加特定的库文件搜索路径 —>相当于指定g++编译器的-L 参数
  # 将/usr/lib/mylibfolder 和 ./lib 添加到库文件搜索路径
  link_directories(/usr/lib/mylibfolder ./lib)
语法:link_directories(dir1 dir2 ...)
  • add_library 生成库文件
  # 通过变量 SRC 生成 libhello.so 共享库
  add_library(hello SHARED ${SRC})
语法:add_library(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 … sourceN)
  • add_compile_options - 添加编译参数
# 添加编译参数 -Wall -std=c++11
add_compile_options(-Wall -std=c++11 -O2)
  • add_executable - 生成可执行文件
  # 编译main.cpp生成可执行文件main
  add_executable(main main.cpp)
语法:add_executable(exename source1 source2 … sourceN)
  • target_link_libraries - 为 target 添加需要链接的共享库 —> 相同于指定g++编译器 − l -l l 参数
  # 将hello动态库文件链接到可执行文件main
  target_link_libraries(main hello)
语法:target_link_libraries(target library1library2…)
  • add_subdirectory - 向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置
# 添加src子目录,src中需有一个CMakeLists.txt
add_subdirectory(src)
语法:add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
  • aux_source_directory - 发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表
# 定义SRC变量,其值为当前目录下所有的源代码文件
aux_source_directory(. SRC)
# 编译SRC变量所代表的源代码文件,生成main可执行文件
add_executable(main ${SRC})
语法:aux_source_directory(dir VARIABLE)
1.2 CMake 常用变量
  • CMAKE_C_FLAGS gcc编译选项

  • CMAKE_CXX_FLAGS g++编译选项

 # 在CMAKE_CXX_FLAGS编译选项后追加-std=c++11
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
  • CMAKE_BUILD_TYPE 编译类型(Debug, Release)
  # 设定编译类型为debug,调试时需要选择debug
  set(CMAKE_BUILD_TYPE Debug) 
  # 设定编译类型为release,发布时需要选择release
  set(CMAKE_BUILD_TYPE Release) 
  • CMAKE_BINARY_DIRPROJECT_BINARY_DIR<projectname>__BINARY_DIR

    • 这三个变量指代的内容是一致的,指的是编译成的二进制目标文件目录。
    • 如果是 in source build,指的就是工程顶层目录。
    • 如果是 out-of-source 编译,指的是工程编译发生的目录。
    • PROJECT_BINARY_DIR 跟其他指令稍有区别,不过现在,你可以理解为他们是一致的。
  • CMAKE_SOURCE_DIRPROJECT_SOURCE_DIR<projectname>__SOURCE_DIR

    • 这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录
    • 也就是在 in source build时,他跟 CMAKE_BINARY_DIR 等变量一致。
    • PROJECT_SOURCE_DIR 跟其他指令稍有区别,现在,你可以理解为他们是一致的。
  • CMAKE_C_COMPILER:指定C编译器

  • CMAKE_CXX_COMPILER:指定C++编译器

  • EXECUTABLE_OUTPUT_PATH:可执行文件输出的存放路径

  • LIBRARY_OUTPUT_PATH:库文件输出的存放路径

2. CMake编译工程

CMake目录结构:项目主目录存在一个CMakeLists.txt文件

2.1 两种方式设置编译规则
  1. 包含源文件的子文件夹包含CMakeLists.txt文件,主目录的CMakeLists.txt通过add_subdirectory添加子目录即可;
  2. 包含源文件的子文件夹未包含CMakeLists.txt文件,子目录编译规则体现在主目录的CMakeLists.txt中;
2.2 编译流程

在 linux 平台下使用 CMake 构建C/C++工程的流程如下

  • 手动编写CMakeLists.txt
  • 执行命令cmake PATH生成Makefile(PATH是顶层CMakeLists.txt 所在的目录)
  • 执行命令make进行编译
2.3 两种构建方式
  • 内部构建(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命令,生成taget
  make

3. CMake 代码实践

代码结构
.
├── CMakeLists.txt
├── include
│   └── swap.h
├── main.cpp
└── src
    ├── CMakeLists.txt
    └── swap.cpp
构建思路:
  1. 将src下面的swap.cpp编译成 共享库 目标文件libswap.so,并将libswap.so放置在项目目录下的bin目录中
  2. 利用外部构建方式。在项目目录下新建build文件夹,进而可以在build下做cmake ..操作
源码
  • main.cpp
#include <iostream>
#include "swap.h"
using namespace std;

int main(int argc, char *argv[])
{
	// int tmp = 5;
	int a = 10;
	int b = 20;
	cout << "Before swap:" << endl;
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;

	swap(a, b);
	cout << "After swap:" << endl;
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	return 0;
}
  • swap.cpp
#include "swap.h"

void swap(int& a, int& b){
	int tmp = a;
	a = b;
	b = tmp;

}
CMakeLists.txt
  • ./CMakeLists.txt
cmake_minimum_required(VERSION 3.2)

project(SWAP)

set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) # 设定库文件输出的存放路径

# 注意添加的顺序
set(CMAKE_BUILD_TYPE Debug) # 设定CMake的构建类型为Debug,发布时可设置为Release

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") #g++编译选项设定

add_subdirectory(${PROJECT_SOURCE_DIR}/src)

add_executable(cmake_main main.cpp)  # 该行要放在下面两行的前面

target_include_directories(cmake_main PRIVATE ${PROJECT_SOURCE_DIR}/include) # 设定目标文件的头文件搜索路径
# 注意,目标文件可通过add_executable或add_library设定

target_link_libraries(cmake_main swap) #目标生成所需链接的库文件
  • ./src/CMakeList.txt
add_library(swap SHARED swap.cpp) # 生成 共享库 目标文件

target_include_directories(swap PRIVATE ${PROJECT_SOURCE_DIR}/include)
构建
  • 下面就是实践的构建操作指令,非常简单
mkdir build
cd build
cmake ..
make
  • 实际的交互输入输出如下:
benjamin@ubuntu:~/CPPDev/g++prac$ mkdir build
benjamin@ubuntu:~/CPPDev/g++prac$ cd build
benjamin@ubuntu:~/CPPDev/g++prac/build$ cmake ..
-- The C compiler identification is GNU 7.5.0
-- The CXX compiler identification is GNU 7.5.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 done
-- Generating done
-- Build files have been written to: /home/benjamin/CPPDev/g++prac/build
benjamin@ubuntu:~/CPPDev/g++prac/build$ make
Scanning dependencies of target swap
[ 25%] Building CXX object src/CMakeFiles/swap.dir/swap.cpp.o
[ 50%] Linking CXX shared library ../../bin/libswap.so
[ 50%] Built target swap
Scanning dependencies of target cmake_main
[ 75%] Building CXX object CMakeFiles/cmake_main.dir/main.cpp.o
[100%] Linking CXX executable cmake_main
[100%] Built target cmake_main
  • 我们再次看看目录层次结构:
benjamin@ubuntu:~/CPPDev/g++prac/build$ cd ..
benjamin@ubuntu:~/CPPDev/g++prac$ tree .
.
├── bin
│   └── libswap.so
├── build
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   │   ├── 3.10.2
│   │   │   ├── CMakeCCompiler.cmake
│   │   │   ├── CMakeCXXCompiler.cmake
│   │   │   ├── CMakeDetermineCompilerABI_C.bin
│   │   │   ├── CMakeDetermineCompilerABI_CXX.bin
│   │   │   ├── CMakeSystem.cmake
│   │   │   ├── CompilerIdC
│   │   │   │   ├── a.out
│   │   │   │   ├── CMakeCCompilerId.c
│   │   │   │   └── tmp
│   │   │   └── CompilerIdCXX
│   │   │       ├── a.out
│   │   │       ├── CMakeCXXCompilerId.cpp
│   │   │       └── tmp
│   │   ├── cmake.check_cache
│   │   ├── CMakeDirectoryInformation.cmake
│   │   ├── cmake_main.dir
│   │   │   ├── build.make
│   │   │   ├── cmake_clean.cmake
│   │   │   ├── CXX.includecache
│   │   │   ├── DependInfo.cmake
│   │   │   ├── depend.internal
│   │   │   ├── depend.make
│   │   │   ├── flags.make
│   │   │   ├── link.txt
│   │   │   ├── main.cpp.o
│   │   │   └── progress.make
│   │   ├── CMakeOutput.log
│   │   ├── CMakeTmp
│   │   ├── feature_tests.bin
│   │   ├── feature_tests.c
│   │   ├── feature_tests.cxx
│   │   ├── Makefile2
│   │   ├── Makefile.cmake
│   │   ├── progress.marks
│   │   └── TargetDirectories.txt
│   ├── cmake_install.cmake
│   ├── cmake_main
│   ├── Makefile
│   └── src
│       ├── CMakeFiles
│       │   ├── CMakeDirectoryInformation.cmake
│       │   ├── progress.marks
│       │   └── swap.dir
│       │       ├── build.make
│       │       ├── cmake_clean.cmake
│       │       ├── CXX.includecache
│       │       ├── DependInfo.cmake
│       │       ├── depend.internal
│       │       ├── depend.make
│       │       ├── flags.make
│       │       ├── link.txt
│       │       ├── progress.make
│       │       └── swap.cpp.o
│       ├── cmake_install.cmake
│       └── Makefile
├── CMakeLists.txt
├── include
│   └── swap.h
├── main.cpp
└── src
    ├── CMakeLists.txt
    └── swap.cpp

15 directories, 53 files

从生成的目录结构可以看出,cmake生成的所有中间文件都在build目录中,这样会使得我们的开发主体结构十分清晰,这就是外部构建的好处。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值