【CMake工具】工具CMake编译轻度使用(C/C++)

目录

CMake编译工具

一、CMake概述

二、CMake的使用

2.1 注释

2.1.1 注释行

2.1.2 注释块

2.2 源文件

2.1.1 共处一室

2.1.2 VIP包房

2.3 私人定制

2.2.1 定义变量

2.2.2 指定使用的C++标准

2.2.3 指定输出的路径

2.4 搜索文件

2.3.1 方式1

2.3.2 方式2

2.5 包含头文件

2.6 制作动态库或静态库

2.6.1 制作静态库

2.6.2 制作静态库

2.6.3 指定输出的路径

2.6.3.1 适用于动态库

2.6.3.2 适用于动态库和静态库

2.7 包含库文件

2.7.1 链接静态库

2.7.2 链接动态库

2.7.2.1 链接系统动态库

2.7.2.2 链接第三方动态库

2.8 日志

2.9 变量操作

2.9.1 使用set拼接

2.9.2 使用list拼接

2.9.3 字符串移除

2.10 宏定义

2.11 预定义宏

2.12 嵌套CMake

2.12.1节点关系

2.12.2 添加子目录

2.12.3 根目录的CMakeLists.txt

2.12.4 calFunc目录的CMakeLists.txt

2.12.5 sotrFunc目录的CMakeLists.txt

2.12.6 test1目录下CMakeLists.txt

2.12.7 test2目录下CMakeLists.txt

2.12.8 构建项目


CMake编译工具

一、CMake概述

        CMake 是一个项目构建工具,并且是跨平台的。关于项目构建我们所熟知的还有Makefile(通过 make 命令进行项目的构建),大多是IDE软件都集成了make,比如:VS 的 nmake、linux 下的 GNU make、Qt 的 qmake等,如果自己动手写 makefile,会发现,makefile 通常依赖于当前的编译平台,而且编写makefile 的工作量比较大,解决依赖关系时也容易出错。

         CMake 恰好能解决上述问题, 其允许开发者指定整个工程的编译流程,在根据编译平台,自动生成本地化的Makefile和工程文件,最后用户只需make编译即可,所以可以把CMake看成一款自动生成 Makefile的工具,其编译流程如下图:

  • 蓝色虚线表示使用makefile构建项目的过程。

  • 红色实线表示使用cmake构建项目的过程。

介绍完CMake的作用之后,再来总结一下它的优点:

  • 跨平台。

  • 能够管理大型项目。

  • 简化编译构建过程和编译过程。

  • 可扩展:可以为 cmake 编写特定功能的模块,扩充 cmake 功能。

二、CMake的使用

        CMake支持大写、小写、混合大小写的命令。如果在编写CMakeLists.txt文件时使用的工具有对应的命提示,那么大小写随缘即可,不要太过在意。

2.1 注释

2.1.1 注释行

CMake使用 # 进行行注释 ,可以放在任何位置。

# 这是一个 CMakeLists.txt 文件
cmake_minimum_required(VERSION 3.0.0)

2.1.2 注释块

#[[ 这是一个 CMakeLists.txt 文件。
这是一个 CMakeLists.txt 文件
这是一个 CMakeLists.txt 文件]]
cmake_minimum_required(VERSION 3.0.0)

2.2 源文件

2.1.1 共处一室

  1. 准备工作,为了方便测试,在我本地电脑准备了这么几个测试文件。

  • 【add.cpp】

#include "head.h"
int Add(int x, int y) { return x + y; }
  • 【sub.cpp】

#include "head.h"
int Sub(int x, int y) { return x - y; }
  • 【mul.cpp】

#include "head.h"
int Mul(int x, int y) { return x * y; }
  • 【div.cpp】

#include "head.h"
int Div(int x, int y) { return x / y; }
  • 【head.h】

#pragma once
#include <iostream>
using namespace std;int Add(int x, int y);
int Sub(int x, int y);
int Mul(int x, int y);
int Div(int x, int y);
  • 【main.cpp】

#include "head.h"int main() 
{int a = 10;int b = 2;cout << a << "+" << b << "=" << Add(a, b) << endl;cout << a << "-" << b << "=" << Sub(a, b) << endl;cout << a << "*" << b << "=" << Mul(a, b) << endl;cout << a << "/" << b << "=" << Div(a, b) << endl;return 0;
}
  • 【目录结构】

[shaxiang@VM-8-14-centos data_structure_algorithm]$ tree .
.
|-- add.cpp
|-- CMakeLists.txt
|-- div.cpp
|-- head.h
|-- main.cpp
|-- mul.cpp
`-- sub.cpp0 directories, 7 files
  1. 添加CMakeLists.txt文件

在上述源文件所在目录下添加一个新文件CMakeLists.txt,文件内容如下:

# 指定使用的 cmake 的最低版本,可选,非必须,如果不加可能会有警告。
cmake_minimum_required(2.8)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
project(test)
# 定义工程会生成一个可执行程序。
add_executable(app main.cpp add.cpp sub.cpp div.cpp mul.cpp)

接下来依次介绍一下在 CMakeLists.txt 文件中添加的三个命令:

  • cmake_minimum_required:指定使用的 cmake 的最低版本,可选,非必须,如果不加可能会有警告

  • project:定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。

  • add_executable:定义工程会生成一个可执行程序。

add_executable(可执行程序名 源文件名称)
  • 这里的可执行程序名和project中的项目名没有任何关系。

  • 源文件名可以是一个也可以是多个,如有多个可用空格或;间隔。

# 样式1
add_executable(app add.c div.c main.c mult.c sub.c)
# 样式2
add_executable(app add.c;div.c;main.c;mult.c;sub.c)
  1. 执行CMake命令

万事俱备只欠东风,将 CMakeLists.txt 文件编辑好之后,就可以执行 cmake 命令了。

[shaxiang@VM-8-14-centos data_structure_algorithm]$ tree .
.
|-- add.cpp
|-- CMakeLists.txt
|-- div.cpp
|-- head.h
|-- main.cpp
|-- mul.cpp
`-- sub.cpp0 directories, 7 files

当执行cmake命令之后,CMakeLists.txt 中的命令就会被执行,所以一定要注意给cmake 命令指定路径的时候一定不能出错。

执行命令之后,看一下源文件所在目录中是否多了一些文件:

[shaxiang@VM-8-14-centos data_structure_algorithm]$ tree -L 1
.
|-- add.cpp
|-- CMakeCache.txt
|-- CMakeFiles
|-- CMakeLists.txt
|-- div.cpp
|-- head.h
|-- main.cpp
|-- mul.cpp
`-- sub.cpp

我们可以看到在对应的目录下生成了一个makefile文件,此时再执行make命令,就可以对项目进行构建得到所需的可执行程序了。

$ make
Scanning dependencies of target app
[ 16%] Building C object CMakeFiles/app.dir/add.c.o
[ 33%] Building C object CMakeFiles/app.dir/div.c.o
[ 50%] Building C object CMakeFiles/app.dir/main.c.o
[ 66%] Building C object CMakeFiles/app.dir/mult.c.o
[ 83%] Building C object CMakeFiles/app.dir/sub.c.o
[100%] Linking C executable app
[100%] Built target app# 查看可执行程序是否已经生成
$ tree -L 1
.
├── add.c
├── app					# 生成的可执行程序
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
├── CMakeLists.txt
├── div.c
├── head.h
├── main.c
├── Makefile
├── mult.c
└── sub.c

最终可执行程序app就被编译出来了(这个名字是在CMakeLists.txt中指定的)。

2.1.2 VIP包房

        通过上面的例子可以看出,如果在CMakeLists.txt文件所在目录执行了cmake命令之后就会生成一些目录和文件(包括 makefile 文件),如果再基于makefile文件执行make命令,程序在编译过程中还会生成一些中间文件和一个可执行文件,这样会导致整个项目目录看起来很混乱,不太容易管理和维护,此时我们就可以把生成的这些与项目源码无关的文件统一放到一个对应的目录里边,比如将这个目录命名为build:

[shaxiang@VM-8-14-centos build]$ cmake ../
-- The C compiler identification is GNU 7.3.1
-- The CXX compiler identification is GNU 7.3.1
-- Check for working C compiler: /opt/rh/devtoolset-7/root/usr/bin/cc
-- Check for working C compiler: /opt/rh/devtoolset-7/root/usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /opt/rh/devtoolset-7/root/usr/bin/c++
-- Check for working CXX compiler: /opt/rh/devtoolset-7/root/usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/shaxiang/data_structure_algorithm/build

        现在cmake命令是在build目录中执行的,但是CMakeLists.txt文件是build目录的上一级目录中,所以cmake 命令后指定的路径为..,即当前目录的上一级目录。

当命令执行完毕之后,在build目录中会生成一个makefile文件。

$ tree build -L 1
build
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
└── Makefile1 directory, 3 files

        这样就可以在build目录中执行make命令编译项目,生成的相关文件自然也就被存储到build目录中了。这样通过cmakemake生成的所有文件就全部和项目源文件隔离开了,各回各家,各找各妈。

[shaxiang@VM-8-14-centos build]$ make
Scanning dependencies of target app
[ 20%] Building CXX object CMakeFiles/app.dir/main.cpp.o
[ 40%] Building CXX object CMakeFiles/app.dir/add.cpp.o
[ 60%] Building CXX object CMakeFiles/app.dir/sub.cpp.o
[ 80%] Building CXX object CMakeFiles/app.dir/div.cpp.o
[100%] Building CXX object CMakeFiles/app.dir/mul.cpp.o
Linking CXX executable app
[100%] Built target app
[shaxiang@VM-8-14-centos build]$ ls
app  CMakeCache.txt  CMakeFiles  cmake_install.cmake  Makefile

2.3 私人定制

2.2.1 定义变量

        在上面的例子中一共提供了5个源文件,假设这五个源文件需要反复被使用,每次都直接将它们的名字写出来确实是很麻烦,此时我们就需要定义一个变量,将文件名对应的字符串存储起来,在cmake里定义变量需要使用set

# SET 指令的语法是:
# [] 中的参数为可选项, 如不需要可以不写
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
  • VAR:变量名

  • VALUE:变量值

# 方式1: 各个源文件之间使用空格间隔
# set(SRC_LIST add.c  div.c   main.c  mult.c  sub.c)# 方式2: 各个源文件之间使用分号 ; 间隔
set(SRC_LIST add.c;div.c;main.c;mult.c;sub.c)
add_executable(app  ${SRC_LIST})

2.2.2 指定使用的C++标准

        在编写C++程序的时候,可能会用到C++11、C++14、C++17、C++20等新特性,那么就需要在编译的时候在编译命令中制定出要使用哪个标准:

$ g++ *.cpp -std=c++11 -o app

        上面的例子中通过参数-std=c++11指定出要使用c++11标准编译程序,C++标准对应有一宏叫做DCMAKE_CXX_STANDARD。在CMake中想要指定C++标准有两种方式:

  1. 在 CMakeLists.txt 中通过 set 命令指定

#增加-std=c++11
set(CMAKE_CXX_STANDARD 11)
#增加-std=c++14
set(CMAKE_CXX_STANDARD 14)
#增加-std=c++17
set(CMAKE_CXX_STANDARD 17)

2. 在执行 cmake 命令的时候指定出这个宏的值

#增加-std=c++11
cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=11
#增加-std=c++14
cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=14
#增加-std=c++17
cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=17

在上面例子中 CMake 后的路径需要根据实际情况酌情修改。

2.2.3 指定输出的路径

在CMake中指定可执行程序输出的路径,也对应一个宏,叫做EXECUTABLE_OUTPUT_PATH,它的值还是通过set命令进行设置:

set(HOME /home/robin/Linux/Sort)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)
  • 第一行:定义一个变量用于存储一个绝对路径。

  • 第二行:将拼接好的路径值设置给EXECUTABLE_OUTPUT_PATH宏,如果这个路径中的子目录不存在,会自动生成,无需自己手动创建。

        由于可执行程序是基于 cmake 命令生成的 makefile 文件然后再执行 make 命令得到的,所以如果此处指定可执行程序生成路径的时候使用的是相对路径 ./xxx/xxx,那么这个路径中的 ./ 对应的就是 makefile 文件所在的那个目录

【根据上面编写出一个编译的项目】

# 指定使用的 cmake 的最低版本,可选,非必须,如果不加可能会有警告。
cmake_minimum_required(VERSION 2.8.12.2)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
project(test)# 设置源文件为变量值SRC需要被执行调用
set(SRC main.cpp add.cpp sub.cpp div.cpp mul.cpp)
# 设置编译标准
set(CMAKE_CXX_STANDARD 98)
# 设置编译输出路径
set(EXECUTABLE_OUTPUT_PATH /home/shaxiang/data_structure_algorithm/build/A/B/C)# 定义工程会生成一个可执行程序。
add_executable(app ${SRC})

2.4 搜索文件

        如果一个项目里边的源文件很多,在编写CMakeLists.txt文件的时候不可能将项目目录的各个文件一一罗列出来,这样太麻烦也不现实。所以,在CMake中为我们提供了搜索文件的命令,可以使用aux_source_directory命令或者file命令。

2.3.1 方式1

在 CMake 中使用aux_source_directory 命令可以查找某个路径下的所有源文件,命令格式为:

aux_source_directory(< dir > < variable >)
  • dir:要搜索的目录。

  • variable:将从dir目录下搜索到的源文件列表存储到该变量中。

// 测试
# 指定使用的 cmake 的最低版本,可选,非必须,如果不加可能会有警告。
cmake_minimum_required(VERSION 2.8.12.2)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
project(test)# 设置源文件为变量值SRC需要被执行调用
# 方法一:路径宏:PROJECT_BINARY_DIR  代表cmake..
aux_source_directory(${PROJECT_SOURCE_DIR} SRC)
# 设置编译标准
set(CMAKE_CXX_STANDARD 98)
# 设置编译输出路径
set(EXECUTABLE_OUTPUT_PATH /home/shaxiang/data_structure_algorithm/build/A/B/C)# 定义工程会生成一个可执行程序。
add_executable(app ${SRC})

2.3.2 方式2

        如果一个项目里边的源文件很多,在编写CMakeLists.txt文件的时候不可能将项目目录的各个文件一一罗列出来,这样太麻烦了。所以,在CMake中为我们提供了搜索文件的命令,他就是file(当然,除了搜索以外通过 file 还可以做其他事情)

file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
  • GLOB: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中。

  • GLOB_RECURSE:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中。

搜索当前目录的src目录下所有的源文件,并存储到变量中

file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)

CMAKE_CURRENT_SOURCE_DIR 宏表示当前访问的 CMakeLists.txt 文件所在的路径

关于要搜索的文件路径和类型可加双引号,也可不加:

// 测试
# 指定使用的 cmake 的最低版本,可选,非必须,如果不加可能会有警告。
cmake_minimum_required(VERSION 2.8.12.2)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
project(test)# 设置源文件为变量值SRC需要被执行调用
file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 设置编译标准
set(CMAKE_CXX_STANDARD 98)
# 设置编译输出路径
set(EXECUTABLE_OUTPUT_PATH /home/shaxiang/data_structure_algorithm/build/A/B/C)# 定义工程会生成一个可执行程序。
add_executable(app ${SRC})

2.5 包含头文件

        在编译项目源文件的时候,很多时候都需要将源文件对应的头文件路径指定出来,这样才能保证在编译过程中编译器能够找到这些头文件,并顺利通过编译。在CMake中设置要包含的目录也很简单,通过一个命令就可以搞定了,他就是include_directories:

include_directories(headpath)

举例说明,有源文件若干,其目录结构如下:

[shaxiang@VM-8-14-centos data_structure_algorithm]$ tree
.
|-- build
|-- CMakeLists.txt
|-- include
|   `-- head.h
`-- src|-- add.cpp|-- div.cpp|-- main.cpp|-- mul.cpp`-- sub.cpp

CMakeLists.txt文件内容如下:

# 指定使用的 cmake 的最低版本,可选,非必须,如果不加可能会有警告。
cmake_minimum_required(VERSION 2.8.12.2)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
project(test)# 设置头文件编译路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 设置源文件编译路径
# set(SRC main.cpp add.cpp sub.cpp div.cpp mul.cpp)
# 方法一:路径宏:PROJECT_BINARY_DIR  代表cmake..
# aux_source_directory(${PROJECT_SOURCE_DIR}/src SRC)
# 方法二:
file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)# 设置编译标准
set(CMAKE_CXX_STANDARD 98)
# 设置编译输出路径
set(EXECUTABLE_OUTPUT_PATH /home/shaxiang/data_structure_algorithm/build/A/B/C)# 定义工程会生成一个可执行程序。
add_executable(app ${SRC})

        其中,第六行指定就是头文件的路径,PROJECT_SOURCE_DIR宏对应的值就是我们在使用cmake命令时,后面紧跟的目录,一般是工程的根目录。

2.6 制作动态库或静态库

        有些时候我们编写的源代码并不需要将他们编译生成可执行程序,而是生成一些静态库或动态库提供给第三方使用,下面来讲解在cmake中生成这两类库文件的方法。

        说明:静态库Linux中[libxxx.so],Windows中[libxxx.dll],动态库Linux中[libxxx.lib],Windows中[libxxx.a]

// 准备生成东静态库的文件
[shaxiang@VM-8-14-centos v2]$ tree
.
|-- build
|-- CMakeLists.txt
|-- include
|   `-- head.h
`-- src|-- add.cpp|-- div.cpp|-- mul.cpp`-- sub.cpp3 directories, 6 files

2.6.1 制作静态库

在cmake中,如果要制作静态库,需要使用的命令如下:

add_library(库名称 STATIC 源文件1 [源文件2] ...) 

        在Linux中,静态库名字分为三部分:lib+库名字+.a,此处只需要指定出库的名字就可以了,另外两部分在生成该文件的时候会自动填充。

        根据上面的目录结构,可以这样编写CMakeLists.txt文件:

# 指定使用的 cmake 的最低版本,可选,非必须,如果不加可能会有警告。
cmake_minimum_required(VERSION 2.8.12.2)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
project(test)# 设置头文件编译路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 设置源文件编译路径
file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
# 设置编译标准
set(CMAKE_CXX_STANDARD 98)# 编译静态库
add_library(cal STATIC ${SRC})

这样最终就会生成对应的静态库文件libcal.a

2.6.2 制作静态库

        在cmake中,如果要制作动态库,需要使用的命令如下:

add_library(库名称 SHARED 源文件1 [源文件2] ...) 

        在Linux中,动态库名字分为三部分:lib+库名字+.so,此处只需要指定出库的名字就可以了,另外两部分在生成该文件的时候会自动填充。

        根据上面的目录结构,可以这样编写CMakeLists.txt文件:

# 指定使用的 cmake 的最低版本,可选,非必须,如果不加可能会有警告。
cmake_minimum_required(VERSION 2.8.12.2)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
project(test)# 设置头文件编译路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 设置源文件编译路径
file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
# 设置编译标准
set(CMAKE_CXX_STANDARD 98)# 编译静态库
add_library(cal SHARED ${SRC})

这样最终就会生成对应的动态库文件libcal.so

2.6.3 指定输出的路径

2.6.3.1 适用于动态库

        对于生成的库文件来说和可执行程序一样都可以指定输出路径。由于在Linux下生成的动态库默认是有执行权限的,所以可以按照生成可执行程序的方式去指定它生成的目录:

# 指定使用的 cmake 的最低版本,可选,非必须,如果不加可能会有警告。
cmake_minimum_required(VERSION 2.8.12.2)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
project(test)# 设置头文件编译路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 设置源文件编译路径
file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
# 设置编译标准
set(CMAKE_CXX_STANDARD 98)# 设置生成东静态库的文件路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
add_library(cal SHARED ${SRC_LIST})

        对于这种方式来说,其实就是通过set命令给EXECUTABLE_OUTPUT_PATH宏设置了一个路径,这个路径就是可执行文件生成的路径。

2.6.3.2 适用于动态库和静态库

        由于在Linux下生成的静态库默认不具有可执行权限,所以在指定静态库生成的路径的时候就不能使用EXECUTABLE_OUTPUT_PATH宏了,而应该使用LIBRARY_OUTPUT_PATH这个宏对应静态库文件和动态库文件都适用

# 指定使用的 cmake 的最低版本,可选,非必须,如果不加可能会有警告。
cmake_minimum_required(VERSION 2.8.12.2)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
project(test)# 设置头文件编译路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 设置源文件编译路径
file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
# 设置编译标准
set(CMAKE_CXX_STANDARD 98)# 设置生成东静态库的文件路径
set(LIBRARY_OUTPUT_PATH /home/shaxiang/data_structure_algorithm/v2/lib)
# 编译静态库
# add_library(cal SHARED ${SRC})
# add_library(cal STATIC ${SRC})

2.7 包含库文件

        在编写程序的过程中,可能会用到一些系统提供的动态库或者自己制作出的动态库或者静态库文件,cmake中也为我们提供了相关的加载动态库的命令。

2.7.1 链接静态库

测试目录结构如下:

[shaxiang@VM-8-14-centos v3]$ tree
.
|-- build
|-- CMakeLists.txt
|-- include
|   `-- head.h
|-- lib
|   |-- libcal.a		// 静态库
|   `-- libcal.so		// 动态库
`-- src`-- main.cpp4 directories, 5 files

在cmake中,链接静态库的命令如下:

link_libraries(<static lib> [<static lib>...])
  • 参数1:指定出要链接的静态库的名字: 可以是全名 libxxx.a,也可以是掐头(lib)去尾(.a)之后的名字 xxx

  • 参数2:要链接的其它静态库的名字。

如果该静态库不是系统提供的(自己制作或者使用第三方提供的静态库)可能出现静态库找不到的情况,此时可以将静态库的路径也指定出来:

link_directories(<lib path>)

这样,修改之后的CMakeLists.txt文件内容如下:

# 指定使用的 cmake 的最低版本,可选,非必须,如果不加可能会有警告。
cmake_minimum_required(VERSION 2.8.12.2)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
project(test)# 设置头文件编译路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 设置源文件编译路径
file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
# 设置编译标准
set(CMAKE_CXX_STANDARD 11)
# 设置包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 连接静态库
link_libraries(cal)
# 生成可执行程序
add_executable(app ${SRC})

添加了第8行的代码,就可以根据参数指定的路径找到这个静态库了。

2.7.2 链接动态库

测试目录结构如下:

[shaxiang@VM-8-14-centos v3]$ tree
.
|-- build
|-- CMakeLists.txt
|-- include
|   `-- head.h
|-- lib
|   |-- libcal.a		// 静态库
|   `-- libcal.so		// 动态库
`-- src`-- main.cpp4 directories, 5 files

在cmake中,链接动态库的命令如下:

target_link_libraries(<target> <PRIVATE|PUBLIC|INTERFACE> <item>... [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
  • target:指定要加载动态库的文件的名字。

    • 该文件可能是一个源文件

    • 该文件可能是一个动态库文件

    • 该文件可能是一个可执行文件

  • PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为 PUBLIC

    • 如果各个动态库之间没有依赖关系,无需做任何设置,三者没有没有区别, 一般无需指定,使用默认的 PUBLIC 即可

    • 动态库的链接具有传递性,如果动态库 A 链接了动态库B、C,动态库D链接了动态库A,此时动态库D相当于也链接了动态库B、C,并可以使用动态库B、C中定义的方法。

    • target_link_libraries(A B C)
      target_link_libraries(D A)
    • PUBLIC:在public后面的库会被Link到前面的target中,并且里面的符号也会被导出,提供给第三方使用。 PRIVATE:在private后面的库仅被link到前面的target中,并且终结掉,第三方不能感知你调了啥库 INTERFACE:在interface后面引入的库不会被链接到前面的target中,只会导出符号。

2.7.2.1 链接系统动态库

动态库的链接和静态库是完全不同的:

  • 静态库会在生成可执行程序的链接阶段被打包到可执行程序中,所以可执行程序启动,静态库就被加载到内存中了。

  • 动态库在生成可执行程序的链接阶段不会被打包到可执行程序中,当可执行程序被启动并且调用了动态库中的函数的时候,动态库才会被加载到内存。

因此,在cmake中指定要链接的动态库的时候,应该将命令写到生成了可执行文件之后

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 添加并指定最终生成的可执行程序名
add_executable(app ${SRC_LIST})
# 指定可执行程序要链接的动态库名字
target_link_libraries(app pthread)

target_link_libraries(app pthread)中:

  • app: 对应的是最终生成的可执行程序的名字。

  • pthread:这是可执行程序要加载的动态库,这个库是系统提供的线程库,全名为libpthread.so,在指定的时候一般会掐头(lib)去尾(.so)。

2.7.2.2 链接第三方动态库

        假设在测试文件main.cpp中既使用了自己制作的动态库libcalc.so又使用了系统提供的线程库,此时CMakeLists.txt文件可以这样写:

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
include_directories(${PROJECT_SOURCE_DIR}/include)
add_executable(app ${SRC_LIST})
target_link_libraries(app pthread calc)

        在第六行中,pthread、calc都是可执行程序app要链接的动态库的名字。当可执行程序app生成之后并执行该文件,会提示有如下错误信息:

$ ./app 
./app: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory

        这是因为可执行程序启动之后,去加载calc这个动态库,但是不知道这个动态库被放到了什么位置,通过命令指定出要链接的动态库的位置,指定静态库位置使用的也是这个命令

link_directories(path)

所以修改之后的CMakeLists.txt文件应该是这样的:

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 指定源文件或者动态库对应的头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 指定要链接的动态库的路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 添加并生成一个可执行程序
add_executable(app ${SRC_LIST})
# 指定要链接的动态库
target_link_libraries(app pthread calc)

        通过link_directories指定了动态库的路径之后,在执行生成的可执行程序的时候,就不会出现找不到动态库的问题了。

温馨提示:使用 target_link_libraries 命令就可以链接动态库,也可以链接静态库文件

2.8 日志

在CMake中可以用用户显示一条消息,该命令的名字为message

message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)
  • (无) :重要消息

  • STATUS :非重要消息

  • WARNING:CMake 警告, 会继续执行

  • AUTHOR_WARNING:CMake 警告 (dev), 会继续执行

  • SEND_ERROR:CMake 错误, 继续执行,但是会跳过生成的步骤

  • FATAL_ERROR:CMake 错误, 终止所有处理过程

        CMake的命令行工具会在stdout上显示STATUS消息,在stderr上显示其他所有消息。CMake的GUI会在它的log区域显示所有消息。

        CMake警告和错误消息的文本显示使用的是一种简单的标记语言。文本没有缩进,超过长度的行会回卷,段落之间以新行做为分隔符。

# 输出一般日志信息
message(STATUS "source path: ${PROJECT_SOURCE_DIR}")
# 输出警告信息
message(WARNING "source path: ${PROJECT_SOURCE_DIR}")
# 输出错误信息
message(FATAL_ERROR "source path: ${PROJECT_SOURCE_DIR}")

2.9 变量操作

2.9.1 使用set拼接

        有时候项目中的源文件并不一定都在同一个目录中,但是这些源文件最终却需要一起进行编译来生成最终的可执行文件或者库文件。如果我们通过file命令对各个目录下的源文件进行搜索,最后还需要做一个字符串拼接的操作,关于字符串拼接可以使用set命令也可以使用list命令。

如果使用set进行字符串拼接,对应的命令格式如下:

set(变量名1 ${变量名1} ${变量名2} ...)

        关于上面的命令其实就是将从第二个参数开始往后所有的字符串进行拼接,最后将结果存储到第一个参数中,如果第一个参数中原来有数据会对原数据就行覆盖。

# 指定使用的 cmake 的最低版本,可选,非必须,如果不加可能会有警告。
cmake_minimum_required(VERSION 2.8.12.2)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
project(test)# 设置头文件编译路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 设置源文件编译路径
file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
# 设置编译标准
set(CMAKE_CXX_STANDARD 11)
# 设置包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 连接静态库
link_libraries(cal)# 拼接字符串练习
set(sTemp Hello_World)
set(strTemp ${sTemp} ${SRC})
message(${sTemp})   # 打印结果:Hello_World
message(${strTemp}) # 打印结果:Hello_World/home/shaxiang/test_cmake/v4/src/main.cpp# 生成可执行程序
add_executable(app ${SRC})

2.9.2 使用list拼接

        如果使用list进行字符串拼接,对应的命令格式如下:

list(APPEND <list> [<element> ...])

        list命令的功能比set要强大,字符串拼接只是它的其中一个功能,所以需要在它第一个参数的位置指定出我们要做的操作,APPEND表示进行数据追加,后边的参数和set就一样了。

# 指定使用的 cmake 的最低版本,可选,非必须,如果不加可能会有警告。
cmake_minimum_required(VERSION 2.8.12.2)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
project(test)# 设置头文件编译路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 设置源文件编译路径
file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
# 设置编译标准
set(CMAKE_CXX_STANDARD 11)
# 设置包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 连接静态库
link_libraries(cal)# 拼接字符串练习
set(sTemp Hello_World )
set(strTemp ${sTemp} ${SRC})
message(${sTemp})   # 打印结果:Hello_World
message(${strTemp}) # 打印结果:Hello_World/home/shaxiang/test_cmake/v4/src/main.cpplist(APPEND sTemp "XXX" "AAA" "QQQ")
message(${sTemp})   # 打印结果:Hello_WorldXXXAAAQQQ# 生成可执行程序
add_executable(app ${SRC})

        在CMake中,使用set命令可以创建一个list。一个在list内部是一个由分号;分割的一组字符串。例如,set(var a b c d e)命令将会创建一个list:a;b;c;d;e,但是最终打印变量值的时候得到的是abcde

2.9.3 字符串移除

        我们在通过file搜索某个目录就得到了该目录下所有的源文件,但是其中有些源文件并不是我们所需要的,比如:

$ tree
.
├── add.cpp
├── div.cpp
├── main.cpp
├── mult.cpp
└── sub.cpp0 directories, 5 files

        在当前这么目录有五个源文件,其中main.cpp是一个测试文件。如果我们想要把计算器相关的源文件生成一个动态库给别人使用,那么只需要add.cpp、div.cp、mult.cpp、sub.cpp这四个源文件就可以了。此时,就需要将main.cpp从搜索到的数据中剔除出去,想要实现这个功能,也可以使用list

list(REMOVE_ITEM <list> <value> [<value> ...])

        通过上面的命令原型可以看到删除和追加数据类似,只不过是第一个参数变成了REMOVE_ITEM

# 指定使用的 cmake 的最低版本,可选,非必须,如果不加可能会有警告。
cmake_minimum_required(VERSION 2.8.12.2)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
project(test)
# 设置编译标准
set(CMAKE_CXX_STANDARD 98)# 设置头文件编译路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 设置源文件编译路径
file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)# 字符串移除练习
# 打印未移除前的字符串
message("删除前:"${SRC})
list(REMOVE_ITEM SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp)
message("删除后:"${SRC})
# 打印结果:
# 删除前:/home/shaxiang/test_cmake/v5/src/main.cpp/home/shaxiang/test_cmake/v5/src/mul.cpp/home/shaxiang/test_cmake/v5/src/sub.cpp/home/shaxiang/test_cmake/v5/src/add.cpp/home/shaxiang/test_cmake/v5/src/div.cpp
# 删除后:/home/shaxiang/test_cmake/v5/src/mul.cpp/home/shaxiang/test_cmake/v5/src/sub.cpp/home/shaxiang/test_cmake/v5/src/add.cpp/home/shaxiang/test_cmake/v5/src/div.cpp# 定义工程会生成一个可执行程序。
add_executable(app ${SRC})

        可以看到,在第8行把将要移除的文件的名字指定给list就可以了。但是一定要注意通过 file 命令搜索源文件的时候得到的是文件的绝对路径(在list中每个文件对应的路径都是一个item,并且都是绝对路径),那么在移除的时候也要将该文件的绝对路径指定出来才可以,否是移除操作不会成功

关于list命令还有其它功能,但是并不常用,在此就不一一进行举例介绍了。

  1. 获取 list 的长度。

list(LENGTH <list> <output variable>)
  • LENGTH:子命令LENGTH用于读取列表长度。

  • list:当前操作的列表。

  • output variable:新创建的变量,用于存储列表的长度。

  1. 读取列表中指定索引的的元素,可以指定多个索引

list(GET <list> <element index> [<element index> ...] <output variable>)
  • list:当前操作的列表。

  • element index:列表元素的索引。

    • 从0开始编号,索引0的元素为列表中的第一个元素。

    • 索引也可以是负数,-1表示列表的最后一个元素,-2表示列表倒数第二个元素,以此类推。

    • 当索引(不管是正还是负)超过列表的长度,运行会报错。

  • output variable:新创建的变量,存储指定索引元素的返回结果,也是一个列表。

  1. 将列表中的元素用连接符(字符串)连接起来组成一个字符串

list (JOIN <list> <glue> <output variable>)
  • list:当前操作的列表。

  • glue:指定的连接符(字符串)。

  • output variable:新创建的变量,存储返回的字符串

  1. 查找列表是否存在指定的元素,若果未找到,返回-1

list(FIND <list> <value> <output variable>)
  • list:当前操作的列表。

  • value:需要再列表中搜索的元素。

  • output variable:新创建的变量。

    • 如果列表<list>中存在<value>,那么返回<value>在列表中的索引。

    • 如果未找到则返回-1。

  1. 将元素追加到列表中

list (APPEND <list> [<element> ...])
  1. 将元素插入到列表的0索引位置

list(INSERT <list> <element_index> <element> [<element> ...])
  1. 将元素插入到列表的0索引位置

list (PREPEND <list> [<element> ...])
  1. 将列表中最后元素移除

list (POP_BACK <list> [<out-var>...])
  1. 将列表中第一个元素移除

list (POP_FRONT <list> [<out-var>...])
  1. 将指定的元素从列表中移除

list (REMOVE_ITEM <list> <value> [<value> ...])
  1. 移指定索引的元素从列表中移除

list (REMOVE_AT <list> <index> [<index> ...])
  1. 移除列表中的重复元素

list (REMOVE_DUPLICATES <list>)
  1. 列表翻转

list(REVERSE <list>)
  1. 列表排序

list (SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
  • COMPARE:指定排序方法。有如下几种值可选:

    • STRING:按照字母顺序进行排序,为默认的排序方法。

    • FILE_BASENAME:如果是一系列路径名,会使用basename进行排序。

    • NATURAL:使用自然数顺序排序。

  • CASE:指明是否大小写敏感。有如下几种值可选:

    • SENSITIVE: 按照大小写敏感的方式进行排序,为默认值。

    • INSENSITIVE:按照大小写不敏感方式进行排序。

  • ORDER:指明排序的顺序。有如下几种值可选:

    • ASCENDING:按照升序排列,为默认值。 DESCENDING:按照降序排列。

2.10 宏定义

在进行程序测试的时候,我们可以在代码中添加一些宏定义,通过这些宏来控制这些代码是否生效,如下所示:

#include "head.h"#define NUM 10
int main() 
{int a = 10;int b = 2;for(int i = 0; i < NUM; i++) {cout << a << "+" << b << "=" << Add(a, b) << endl;cout << a << "-" << b << "=" << Sub(a, b) << endl;cout << a << "*" << b << "=" << Mul(a, b) << endl;cout << a << "/" << b << "=" << Div(a, b) << endl;}#ifdef DEBUGcout << "我是一个程序源哦!" << endl;
#endifreturn 0;
}

在程序的第七行对DEBUG宏进行了判断,如果该宏被定义了,那么第八行就会进行日志输出,如果没有定义这个宏,第八行就相当于被注释掉了,因此最终无法看到日志输入出(上述代码中并没有定义这个宏)。

为了让测试更灵活,我们可以不在代码中定义这个宏,而是在测试的时候去把它定义出来,其中一种方式就是在gcc/g++命令中去指定,如下:

$ gcc test.c -DDEBUG -o app

gcc/g++命令中通过参数 -D指定出要定义的宏的名字,这样就相当于在代码中定义了一个宏,其名字为DEBUG

CMake中我们也可以做类似的事情,对应的命令叫做add_definitions:

add_definitions(-D宏名称)

针对于上面的源文件编写一个CMakeLists.txt,内容如下:

# 指定使用的 cmake 的最低版本,可选,非必须,如果不加可能会有警告。
cmake_minimum_required(VERSION 2.8.12.2)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
project(test)
# 设置编译标准
set(CMAKE_CXX_STANDARD 98)# 设置头文件编译路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 设置源文件编译路径
file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)# 自定义的宏
add_definitions(-DDEBUG)
# 定义工程会生成一个可执行程序。
add_executable(app ${SRC})

2.11 预定义宏

下面的列表中为大家整理了一些CMake中常用的宏:

功能
PROJECT_SOURCE_DIR使用cmake命令后紧跟的目录,一般是工程的根目录
PROJECT_BINARY_DIR执行cmake命令的目录
CMAKE_CURRENT_SOURCE_DIR当前处理的CMakeLists.txt所在的路径
CMAKE_CURRENT_BINARY_DIRtarget 编译目录
EXECUTABLE_OUTPUT_PATH重新定义目标二进制可执行文件的存放位置
LIBRARY_OUTPUT_PATH重新定义目标链接库文件的存放位置
PROJECT_NAME返回通过PROJECT指令定义的项目名称
CMAKE_BINARY_DIR项目实际构建路径,假设在build目录进行的构建,那么得到的就是这个目录的路径

2.12 嵌套CMake

        如果项目很大,或者项目中有很多的源码目录,在通过CMake管理项目的时候如果只使用一个CMakeLists.txt,那么这个文件相对会比较复杂,有一种化繁为简的方式就是给每个源码目录都添加一个CMakeLists.txt文件(头文件目录不需要),这样每个文件都不会太复杂,而且更灵活,更容易维护。

先来看一下下面的这个的目录结构:

[shaxiang@VM-8-14-centos v6]$ tree
.
|-- bin
|-- calFunc
|   |-- add.cpp
|   |-- CMakeLists.txt
|   |-- div.cpp
|   |-- mul.cpp
|   `-- sub.cpp
|-- CMakeLists.txt
|-- include
|   `-- head.h
|-- lib
|-- sortFunc
|   |-- bubbleSort.cpp
|   `-- CMakeLists.txt
|-- test1
|   |-- CMakeLists.txt
|   `-- sort.cpp
`-- test2|-- cal.cpp`-- CMakeLists.txt7 directories, 13 files

2.12.1节点关系

        众所周知,Linux的目录是树状结构,所以嵌套的 CMake 也是一个树状结构,最顶层的 CMakeLists.txt 是根节点,其次都是子节点。因此,我们需要了解一些关于 CMakeLists.txt 文件变量作用域的一些信息:

  • 根节点CMakeLists.txt中的变量全局有效。

  • 父节点CMakeLists.txt中的变量可以在子节点中使用。

  • 子节点CMakeLists.txt中的变量只能在当前节点中使用。

2.12.2 添加子目录

        接下来我们还需要知道在 CMake 中父子节点之间的关系是如何建立的,这里需要用到一个 CMake 命令:

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
  • source_dir:指定了CMakeLists.txt源文件和代码文件的位置,其实就是指定子目录。

  • binary_dir:指定了输出文件的路径,一般不需要指定,忽略即可。

  • EXCLUDE_FROM_ALL:在子路径下的目标默认不会被包含到父路径的ALL目标里,并且也会被排除在IDE工程文件之外。用户必须显式构建在子路径下的目标。

通过这种方式CMakeLists.txt文件之间的父子关系就被构建出来了。

2.12.3 根目录的CMakeLists.txt

# 指定CMake版本的最低版本
cmake_minimum_required(VERSION 2.8.12.2)
# 指定项目名称
project(TestDeme)# 指定头文件路径
set(HEADPATH ${PROJECT_SOURCE_DIR}/include)
# 指定静态库生成的路径
set(LIBPATH ${PROJECT_SOURCE_DIR}/lib)
# 指定可执行生成的路径
set(EXEPATH ${PROJECT_SOURCE_DIR}/bin)# 指定库文件名称
set(CALCLIB calc)
set(SORTLIB sort)
# 指定可执行程序名称
set(APPNAME1 app1)
set(APPNAME2 app2)# 给当前节点添加子目录 
add_subdirectory(calFunc)
add_subdirectory(sortFunc)
add_subdirectory(test1)
add_subdirectory(test2)

2.12.4 calFunc目录的CMakeLists.txt

# 指定CMake版本的最低版本
cmake_minimum_required(VERSION 2.8.12.2)
# 指定项目名称
project(Calc)# 指定头文件目录
include_directories(${HEADPATH})
# 搜索源文件
aux_source_directory(./ SRC)
# 指定静态库生成路径
set(LIBRARY_OUTPUT_PATH ${LIBPATH})
# 生成静态库
add_library(${CALCLIB} STATIC ${SRC})

2.12.5 sotrFunc目录的CMakeLists.txt

# 指定CMake版本的最低版本
cmake_minimum_required(VERSION 2.8.12.2)
# 指定项目名称
project(Sort)# 指定头文件目录
include_directories(${HEADPATH})
# 搜索源文件
aux_source_directory(./ SRC)
# 指定静态库生成路径
set(LIBRARY_OUTPUT_PATH ${LIBPATH})
# 生成静态库
add_library(${SORTLIB} STATIC ${SRC})

2.12.6 test1目录下CMakeLists.txt

# 指定CMake版本的最低版本
cmake_minimum_required(VERSION 2.8.12.2)
# 指定项目名称
project(Test1)# 指定头文件路径
include_directories(${HEADPATH})
# 搜索源文件
aux_source_directory(./ SRC)
# 指定连接静态库的路径
link_directories(${LIBPATH})
# 连接静态库
link_libraries(${SORTLIB})
# 设置生成可执行文件的路径
set(EXECUTABLE_OUTPUT_PATH ${EXEPATH})
# 生成可执行文件
add_executable(${APPNAME1} ${SRC})

2.12.7 test2目录下CMakeLists.txt

# 指定CMake版本的最低版本
cmake_minimum_required(VERSION 2.8.12.2)
# 指定项目名称
project(Test2)# 指定头文件路径
include_directories(${HEADPATH})
# 搜索源文件
aux_source_directory(./ SRC)
# 指定连接静态库的路径
link_directories(${LIBPATH})
# 连接静态库
link_libraries(${CALCLIB})
# 设置生成可执行文件的路径
set(EXECUTABLE_OUTPUT_PATH ${EXEPATH})
# 生成可执行文件
add_executable(${APPNAME2} ${SRC})

2.12.8 构建项目

[shaxiang@VM-8-14-centos v6]$ cd build/
[shaxiang@VM-8-14-centos build]$ cmake ..
-- The C compiler identification is GNU 7.3.1
-- The CXX compiler identification is GNU 7.3.1
-- Check for working C compiler: /opt/rh/devtoolset-7/root/usr/bin/cc
-- Check for working C compiler: /opt/rh/devtoolset-7/root/usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /opt/rh/devtoolset-7/root/usr/bin/c++
-- Check for working CXX compiler: /opt/rh/devtoolset-7/root/usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/shaxiang/test_cmake/v6/build
[shaxiang@VM-8-14-centos build]$ make
Scanning dependencies of target calc
[ 14%] Building CXX object calFunc/CMakeFiles/calc.dir/mul.cpp.o
[ 28%] Building CXX object calFunc/CMakeFiles/calc.dir/sub.cpp.o
[ 42%] Building CXX object calFunc/CMakeFiles/calc.dir/add.cpp.o
[ 57%] Building CXX object calFunc/CMakeFiles/calc.dir/div.cpp.o
Linking CXX static library ../../lib/libcalc.a
[ 57%] Built target calc
Scanning dependencies of target sort
[ 71%] Building CXX object sortFunc/CMakeFiles/sort.dir/bubbleSort.cpp.o
Linking CXX static library ../../lib/libsort.a
[ 71%] Built target sort
Scanning dependencies of target app1
[ 85%] Building CXX object test1/CMakeFiles/app1.dir/sort.cpp.o
Linking CXX executable ../../bin/app1
[ 85%] Built target app1
Scanning dependencies of target app2
[100%] Building CXX object test2/CMakeFiles/app2.dir/cal.cpp.o
Linking CXX executable ../../bin/app2
[100%] Built target app2
[shaxiang@VM-8-14-centos v6]$ tree -L 2
.
|-- bin
|   |-- app1
|   `-- app2
|-- build
|   |-- calFunc
|   |-- CMakeCache.txt
|   |-- CMakeFiles
|   |-- cmake_install.cmake
|   |-- Makefile
|   |-- sortFunc
|   |-- test1
|   `-- test2
|-- calFunc
|   |-- add.cpp
|   |-- CMakeLists.txt
|   |-- div.cpp
|   |-- mul.cpp
|   `-- sub.cpp
|-- CMakeLists.txt
|-- include
|   `-- head.h
|-- lib
|   |-- libcalc.a
|   `-- libsort.a
|-- sortFunc
|   |-- bubbleSort.cpp
|   `-- CMakeLists.txt
|-- test1
|   |-- CMakeLists.txt
|   `-- sort.cpp
`-- test2|-- cal.cpp`-- CMakeLists.txt13 directories, 20 files

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/103267.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

岩土工程安全监测利器:振弦采集仪的发展

岩土工程安全监测利器&#xff1a;振弦采集仪的发展 岩土工程安全监测是保障建筑物、地下工程和地质环境安全稳定运行的重要手段。传统上&#xff0c;监测手段主要依靠人工巡视以及基础设施安装的传感器&#xff0c;但是这些方法都存在着缺陷。人工巡视存在的问题是数据采集精…

云原生Kubernetes:pod基础

目录 一、理论 1.pod 2.pod容器分类 3.镜像拉取策略&#xff08;image PullPolicy&#xff09; 二、实验 1.Pod容器的分类 2.镜像拉取策略 三、问题 1.apiVersion 报错 2.pod v1版本资源未注册 3.取行显示指定pod信息 四、总结 一、理论 1.pod (1) 概念 Pod是ku…

CASAIM与南京航空航天大学在自动化叶片曲面分析系统开展合作,推动航空航天发动机零部件自动化3D检测进程

近期&#xff0c;CASAIM与南京航空航天大学在自动化叶片曲面分析系统展开深入合作&#xff0c;充分发挥双方在航空航天和智能检测领域优势&#xff0c;共同推动航空航天发动机零部件自动化3D检测进程。 南京航空航天大学创建于1952年10月&#xff0c;是新中国自己创办的第一批…

软考高级架构师下篇-14面向服务架构设计理论

目录 1. 引言2. SOA的相关概念3. SOA的发展历史4. SOA的参考架构5. SOA 主要协议和规范6. SOA设计的标准要求7. SOA的作用与设计原则8. SOA的设计模式9. SOA构建与实施10. 前文回顾1. 引言 在面向服务的体系结构(Service-Oriented Architecture,SOA)中,服务的概念有了延伸…

视频监控/安防监控/AI视频分析/边缘计算/TSINGSEE青犀AI算法智慧仓储解决方案

随着全球经济与科学技术的双重推动&#xff0c;我国的仓储管理已经进入了高速发展时期&#xff0c;物流仓储也由简单的储藏仓库向智能化仓储转变。TSINGSEE青犀AI智慧仓储解决方案是利用先进的信息技术和物联网技术来提高仓储管理效率、降低成本的一种仓储管理模式。 方案功能 …

C++——类与对象(下篇)

前言 前面已经介绍了类与对象&#xff08;上&#xff09;&#xff0c;类与对象&#xff08;中&#xff09;的两篇文章&#xff0c;下面是类与对象的最后一些重要知识点的介绍和总结。 目录 再谈构造函数Static成员友元内部类匿名对象拷贝对象时的一些编译器优化再次理解封装…

2023高教社杯数学建模E题思路代码 - 黄河水沙监测数据分析

# 1 赛题 E 题 黄河水沙监测数据分析 黄河是中华民族的母亲河。研究黄河水沙通量的变化规律对沿黄流域的环境治理、气候变 化和人民生活的影响&#xff0c; 以及对优化黄河流域水资源分配、协调人地关系、调水调沙、防洪减灾 等方面都具有重要的理论指导意义。 附件 1 给出了位…

Jmeter系列-插件安装(5)

前言 jmeter4.0以上&#xff0c;如现在最新的5.2.1版本是有集成插件的只需要在官网下载 plugins-manager.jar 包&#xff0c;放在jmeter安装路径的lib/ext目录下即可使用&#xff1a;https://jmeter-plugins.org/install/Install/但并不能满足所有需求&#xff0c;仍然需要安装…

[小尾巴 UI 组件库] 全屏响应式轮播背景图(基于 Vue 3 与 Element Plus)

文章归档于&#xff1a;https://www.yuque.com/u27599042/row3c6 组件库地址 npm&#xff1a;https://www.npmjs.com/package/xwb-ui?activeTabreadme小尾巴 UI 组件库源码 gitee&#xff1a;https://gitee.com/tongchaowei/xwb-ui小尾巴 UI 组件库测试代码 gitee&#xff1a…

解锁“文心一言”赋能大型连锁商超密码

解锁“文心一言”赋能大型连锁商超密码](https://mp.weixin.qq.com/s/rgj9sZFiajxc5EBVvnMH8Q) 2023年&#xff0c;“AIGC”江湖风起云涌&#xff0c;前有百度“文心一言”对标ChatGPT全民开放&#xff0c;后有其他公司的大模型迅速跟进落地&#xff0c;譬如某飞。 大模型这部…

功能测试常用的测试用例大全

登录、添加、删除、查询模块是我们经常遇到的&#xff0c;这些模块的测试点该如何考虑 1)登录 ① 用户名和密码都符合要求(格式上的要求) ② 用户名和密码都不符合要求(格式上的要求) ③ 用户名符合要求&#xff0c;密码不符合要求(格式上的要求) ④ 密码符合要求&#xff0c;…

Linux下C语言使用 netlink sockets与内核模块通信

netlink简介 Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。在Linux标准内核中&#xff0c;系统默认集成了很多netlink实例&#xff0c;比如日志上报、路由系统等&#xff0c;netlink消息是双向的&a…