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

news2025/2/27 0:00:00

目录

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.cpp

0 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.cpp

0 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
└── Makefile

1 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.cpp

3 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.cpp

4 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.cpp

4 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.cpp

list(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.cpp

0 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 DEBUG
    cout << "我是一个程序源哦!" << endl;
#endif
    return 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.txt

7 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.txt

13 directories, 20 files

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CRM软件系统能否监控手机的使用

CRM可以监控手机吗&#xff1f;答案是不可以。CRM是一款帮助企业优化业务流程&#xff0c;提高销售效率的工具。例如Zoho CRM&#xff0c;最多也就是听一下销售的通话录音&#xff0c;却不可以监控手机&#xff0c;毕竟CRM不是一款监控软件。 CRM的主要作用有以下几点&#xf…

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

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

聚观早报|华为Mate 60 Pro支持面容支付;特斯拉重回底特律车展

【聚观365】9月8日消息 华为Mate 60 Pro已支持面容支付 特斯拉将重回底特律车展 iPhone在美国有1.67亿用户 韩国半导体8月份出口85.6亿美元 比亚迪元PLUS冠军版将于9月15日上市 华为Mate 60 Pro已支持面容支付 毫无预热的华为Mate 60 Pro突然在华为商城首批开售&#xf…

Ansys Zemax | 手机镜头设计 - 第 3 部分:使用 STAR 模块和 ZOS-API 进行 STOP 分析

本文是 3 篇系列文章的一部分&#xff0c;该系列文章将讨论智能手机镜头模组设计的挑战&#xff0c;从概念、设计到制造和结构变形的分析。本文是三部分系列的第三部分。它涵盖了使用 Ansys Zemax OpticStudio Enterprise 版本提供的 STAR 技术对智能手机镜头进行自动的结构、热…

微信小程序的开发---tabBar的介绍

目录 一、tabBar的介绍 二、tabBar的6个组成部分 三、tabBar节点的配置项 四、tab项的配置选项 五、tabBar的使用 一、tabBar的介绍 tabBar是移动端应用常见的页面效果&#xff0c;用于实现多页面的快速切换。小程序中通常将其分为&#xff1a; &#xff08;1&#xff09;…

猫头虎解析:如何巧妙避免GET请求中的“EOF“错误?

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

企业架构LNMP学习笔记22

防盗链原理和实现。 域名A的资源文件&#xff0c;经常被域名B直接调用访问。 而用户经常访问域名B&#xff0c;看到的资源&#xff08;图片等&#xff09;以为是域名B的&#xff0c;实际则是域名A的。 但是域名A没有获得任何收益&#xff0c;却要给域名B来源的访问消耗服务器…

解决eclipse的报错:Must declare a named package because this compilation

刚安装完成eclipse&#xff0c; 创建类的时候报错 报错信息如下&#xff1a; 原因&#xff1a;新版本的ECLIPSE要求每一个类都必须定义在包里面 解决方法&#xff1a;创建类的时候指定类的名字&#xff0c;如下图&#xff1a;Package 里面填写ch3&#xff0c;表示包名 创建完成…

智慧物联网医疗,树立可持续性智能化和便利化新标杆

医疗物联网&#xff08;IoMT&#xff09;是一个快速发展的领域&#xff0c;它正在改变医疗保健的未来。IoMT 指的是物理设备、车辆、家用电器和其他嵌入传感器、软件和网络连接的对象的网络。这些设备可以帮助医疗保健提供者更好地了解患者的状况&#xff0c;并提供更高效和更有…

区分何时使用内连接或者外连接

一张表(A)中的一条记录可以对应另一张表(B)中的多条记录; 另一张表(B)中的一条记录只能对应一张表(A)中的一条记录。 //查询所有姓张的学生的id,name和所在班级name 这里如果外键可以为空的话就需要使用左外连接&#xff0c;才可以查到。 内连接 //假如外键不可以为空select…

Canape使用中遇到问题的解决办法

问题一、canape绘制目标时&#xff0c;二维结构体变量只能录制16个的解决办法 打开ASAP2 Studio 2.6 -Expert 软件。 把该软件中的设置项如下图进行修改。 然后用ASAP2 Studio 2.6 -Expert 软件 打开elf文件导出成A2L文件。 最后关闭该软件。 再在canape工程中重新加载刚才…

1.8 工程相关解析(各种文件,资源访问

目录 1.8 工程相关解析(各种文件&#xff0c;资源访问) 分类 Android 基础入门教程 本节引言&#xff1a; 1.工程项目结构解析&#xff1a; 1.res资源文件夹介绍&#xff1a; 2.如何去使用这些资源 2.深入了解三个文件&#xff1a; MainActivity.java&#xff1a; 布局…

指针-成绩统计

任务描述 请设计一个函数&#xff0c;以指针变量作为函数的参数&#xff0c;从标准输入上读入数量不定的成绩。统计全班成绩的总数量、90 分及以上成绩的个数、60 分及以上成绩的个数和全班成绩的平均分&#xff0c;并返回这四个数给主程序。编写主程序调用此函数完成统计功能…

gitlab 点击Integrations出现500错误

背景&#xff1a;在新服务器重新搭建了gitlab&#xff0c;并导入原来gitlab的备份&#xff0c;在项目中点击点击Integrations出现500错误。 解决方法&#xff1a;1.进入新服务器&#xff0c;将 /etc/gitlab/gitlab-secrets.json重命名为 /etc/gitlab/gitlab-secrets.json.bak …

快速入门 Logback

简介 Logback 旨在作为流行的 log4j 项目的继承者。它是由 log4j 创始人 Ceki Gulcu 设计的。同时它也是 SpingBoot 项目的默认日志框架。 安装 因为 logback 需要和 slf4j 一起使用&#xff0c;所以总共需要添加依赖的包有 slf4j-api.jar&#xff0c;logback-core.jar&…

sql:SQL优化知识点记录(十四)

&#xff08;1&#xff09;索引失效行锁变表锁 建立2个索引 索引是失效后&#xff0c;系统性能会变查&#xff0c;如果涉及到锁的话&#xff0c;行锁会变表锁 有一个问题&#xff0c;当session1用b字段做查询条件因为是varchar类型&#xff0c;需要加双引号&#xff0c;但是没…

开源知识库平台Raneto

什么是 Raneto &#xff1f; Raneto 是一个开源知识库平台&#xff0c;它使用静态 Markdown 文件来支持您的知识库。 官方提供了 doc & demo 网站&#xff0c;即是帮助文档&#xff0c;也是个 demo&#xff0c;地址&#xff1a;https://docs.raneto.com 准备 项目使用con…

摆动输入连杆夹持机构

1、运动与受力分析 import sympy as sy import numpy as np import matplotlib.pyplot as plt a,a1,b,b1,c,c1,d2,d3,fi,F,L,e sy.symbols(a,a1,b,b1,c,c1,d2,d3,fi,F,L,e)A(-d2,0) D(0,d3) B(-d2a*cos(fi),a*sin(fi)) C(-c*cos(pu),d3c*sin(pu)) B(-d2a*cos(fipi),a*sin(fipi…

K8S原理架构与实战教程

文章目录 一、背景1.1 物理机时代、虚拟机时代、容器化时代1.2 容器编排的需要 二、K8S架构2.2 Worker节点 三、核心概念3.1 Pod3.2 Deployment3.3 Service3.4 Volume3.5 Namespace 四、K8S安装五、kubectl常用命令六、K8S实战6.1 水平扩容6.2 自动装箱6.2.1 节点污点6.2.2 Pod…

Java多线程(二)线程安全

线程安全 线程安全&#xff08;风险&#xff09; 线程不安全的原因&#xff1a; 解决线程不安全&#xff1a; synchronized 内存刷新 可重入 volatile 关键字 wait 和 notify wait&#xff08;&#xff09; notify &#xff08;&#xff09; wait与sleep的区别&…