1.2 cmake基本使用

620 阅读3分钟

Cmake概述

什么是cmake

cmake是一个跨平台的安装编译工具,可以用简单的语言来描述所有平台的c++工程安装

为什么要用cmake

在1.1文章中我们已经学习了最原始的c++工程编译方式,由于我们的示例工程很简单,所以使用原生的编译命令行并没有那么难,但是当我们的工程复杂度提高,使用原生方式编译很繁琐,而且部署到不同的平台时,我们需要对编译命令进行修改,此时cmake横空出世,它能帮助我们简化编译命令,并且实现一次书写,各个平台都可复用。

cmake安装

见文章juejin.cn/post/714380…

cmake语法格式

模板

指令(参数1 参数2 参数3...)

  • 使用()将参数括起来,参数之间使用空格或者分号隔开

  • 使用${变量名}的方式取变量值,但是在if语句中直接使用变量名取值

  • 指令不区分大小写,参数和变量名区分大小写

cmake重要命令

  • cmake_minimum_required

指定CMake的最小版本要求:

cmake_minimum_required(VERSION 2.8.1)

完整语法**cmake_minimum_required(VERSION versionNumber [FATAL_ERROR])**

  • project

指定工程名称:

project(HelloWorld)

完整语法**project(projectname [CXX] [C] [Java])**

  • set

定义变量

# 定义src变量的值为swap.cpp main.cpp
set(src swap.cpp main.cpp)

完整语法**set(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])**

  • include_directories

指定多个头文件的搜索目录,相当于g++编译命令中的-I参数

# 添加./include和/usr/include/myInclude到头文件搜索路径中
include_directories(./include /usr/include/myInclude)

完整语法**include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 …)**

  • target_include_directories

指定目标包含的头文件路径,目标 由 add_library()或 add_executable()生成。

  • link_directories

向工程添加多个特定的库文件搜索路径,相当于指定g++编译器的-L参数

# 添加/usr/lib/myLib ./lib到库文件搜索路径中
link_directories(/usr/lib/myLib ./lib)
  • 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 -O2编译参数
add_compile_options(-Wall -std=c++11 -O2)
  • add_executable

生成可执行文件

# 编译main.cppsrc/swap.cpp生成可执行文件main
add_executable(main main.cpp src/swap.cpp)
  • target_link_libraries

为目标文件链接指定库

# 将libhello.so动态库文件链接到可执行文件main
target_link_libraries(main hello)
  • aux_source_directory

发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表

# 定义SRC变量,其值为当前目录下所有的源代码文件
aux_source_directory(. SRC)
# 编译SRC变量所代表的源代码文件,生成main可执行文件
add_executable(main ${SRC})

cmake重要变量

  • 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_C_COMPILER

指定C编译器

  • CMAKE_CXX_COMPILER

指定C++编译器

  • EXECUTABLE_OUTPUT_PATH

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

  • LIBRARY_OUTPUT_PATH

库文件输出的存放路径

  • PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR

参考: stormzhou.blog.csdn.net/article/det…

Cmake编译工程

在项目的主目录下要有一个CMakeLists.txt文件

两种方式设置编译规则:

  1. 包含源文件的子文件夹包含CMakeLists.txt文件,主目录的CMakeLists.txt通过add_subdirectory添加子目录即可;
  2. 包含源文件的子文件夹未包含CMakeLists.txt文件,子目录编译规则体现在主目录的CMakeLists.txt中。

编译基本流程

  1. 编写CMakeLists.txt文件
  2. 执行cmake PATH生成MakeFile(PATH是顶层CMakeLists.txt所在的目录)
  3. 执行make命令进行编译

两种构建方式

  • 内部构建

不推荐使用。该构建方式在同级目录下构建,会产生一大堆中间文件,让项目工程显的很杂乱。

  • 外部构建

在工程主目录下先新建一个build文件,切换到build目录下执行cmake..和make。

mkdir build 
cd build 
cmake ..
make

项目练习

HelloWorld项目

CMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project(helloworld)
add_executable(helloMain main.cpp)

编译前:

image.png

cd build && cmake .. && make

编译后:

image.png

多目录工程——直接编译

编译前的目录结构:

image.png

CMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project(SWAP)
include_directories(include) 
add_executable(fromCmakeBin main.cpp src/swap.cpp)

cd build && cmake .. && make

编译后的目录结构:

image.png

多目录工程——生成库编译

我们要实现的编译过程如下:

  • 将src目录下的swap.cpp编译为libswap.a的静态库
  • 然后将main.cpp编译为可执行文件StaticMain
  • 将libswap.a链接到目标文件StaticMain
  • StaticMain输出到build/bin目录下

编译前的目录结构:

image.png

swap.h

#pragma once

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"
#include <iostream>

void swap::run()
{
    int temp;
    temp = this->_a;
    this->_a = this->_b;
    this->_b = temp;
}

void swap::printInfo()
{
    std::cout << "_a:" << this->_a << std::endl;
    std::cout << "_b:" << this->_b << std::endl;
}

main.cpp

#include "swap.h"

int main(int argc, char **argv){
    swap mySwap(109, 90); 
    mySwap.printInfo();
    mySwap.run();
    mySwap.printInfo();
    return 0;
}

CMakelists.txt

# 指定cmake版本要求
cmake_minimum_required(VERSION 3.0)

# 项目名称
project(SWAP_LIBRARY)

# 添加编译选项
add_compile_options(-Wall -std=c++11)

# 设置编译类型,发布Debug版本
set(CMAKE_BUILD_TYPE Debug)

# 设置最终可执行文件的输出目录
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)

# 将src/swap.cpp编译为静态库libswap.a
add_library(swap STATIC src/swap.cpp)

# 指定libswap.a的头文件搜索目录
target_include_directories(swap PUBLIC ${PROJECT_SOURCE_DIR}/include)

# 将main.cpp编译为可执行文件StaticMain
add_executable(StaticMain main.cpp)

# 将libswap.a链接到StaticMain
target_link_libraries(StaticMain swap)

命令行执行CMakeLists.txt

cd build 
cmake ..
make

cmake编译后的文件目录:

image.png

一些坑以及疑惑

1.win平台报错cmake ..报错 image.png

解决方案:加上-G "MinGW Makefiles"

cmake .. -G "MinGW Makefiles"

参考文章

mp.weixin.qq.com/s/nmjQIezDb…