CMake 入门教程之四

大纲

前言

本文将介绍 CMake 如何编译和链接静态库和动态库。

C/C++ 项目编译的介绍

编译的流程

C++ 的编译和链接过程通常分为四个主要阶段:预处理、编译、汇编和链接。每个阶段都有特定的任务,最终生成可执行文件。这些阶段的流程如下:

  • 预处理:处理 #include#define 等指令,生成纯文本代码。
  • 编译:将代码转换成汇编代码。
  • 汇编:将汇编代码转换成机器代码,生成目标文件。
  • 链接:将目标文件和库文件组合,生成可执行文件。

编译的命令

步骤命令
1. 预处理 gcc -E hello.c -o hello.i
2. 编译到汇编代码 gcc -S hello.c -o hello.s
3. 汇编到目标代码(机器语言)gcc -c hello.s -o hello.o
4. 链接,生成可执行文件 gcc hello.o -o hello
以上四个步骤,可以合成一个步骤,直接编译链接成可执行的目标文件 gcc hello.c -o hello

静态库与动态库的介绍

静态库的介绍

  • 静态库的概念

    • 静态库基本可以理解为源码编译后的二进制代码,也就是多个编译后的目标文件的集合
    • Windows 平台编译后的目标文件的文件名后缀为 .obj,而 Linux 或者 MacOS 平台的文件名后缀为 .o
    • 引用开源项目的静态库时,必须注意遵守开源协议,也就是针对相应的静态库是否获得了使用授权
  • 静态库的文件名规则

    • Windows 平台:以 .lib 为文件名后缀,比如 xlog.lib,或者代表 Debug 版本的 xlog_d.lib
    • Liunx 平台(Ubuntu、Android、HarmonyOS):以 lib 为文件名前缀,并且以 .a 为文件名后缀,比如 libxlog.a
    • MacOS 平台:以 lib 为文件名前缀,并且以 .a 为文件名后缀,比如 libxlog.a

动态库的介绍

  • 动态库的文件名规则
    • Windows 平台:
      • 第一类文件,以 .lib 为文件名后缀,比如 xlog.lib,存放的是函数地址索引
      • 第二类文件,以 .dll 为文件名后缀,比如 xlog.dll,存放的是函数二进制代码
    • Liunx 平台(Ubuntu、Android、HarmonyOS):以 lib 为文件名前缀,并且以 .so 为文件名后缀,比如 libxlog.so
    • MacOS 平台:以 lib 为文件名前缀,并且以 .dylib 为文件名后缀,比如 libxlog.dylib

头文件的作用

  • 函数名称和参数类型(用于索引查找函数地址)
  • 不引用,可以自己直接声明函数
  • 知道函数名称后,就可以调用系统 API 查找函数

CMake 编译与链接静态库

代码下载

完整的 CMake 案例代码可以从 这里 下载得到。

CMake 编译静态库

这里将演示如何使用 CMake 编译生成 C/C++ 项目的静态库。

  • 项目的目录结构
1
2
3
4
5
cmake_lib
└── xlog
├── CMakeLists.txt
├── xlog.cpp
└── xlog.h
  • xlog 目录下的 xlog.h 源文件
1
2
3
4
5
6
7
8
9
10
11
#ifndef XLOG_H
#define XLOG_H

class XLog {

public:
XLog();

};

#endif
  • xlog 目录下的 xlog.cpp 源文件
1
2
3
4
5
6
7
8
#include <iostream>
#include "xlog.h"

using namespace std;

XLog::XLog() {
cout << "Create XLog Instance" << endl;
}
  • xlog 目录下的 CMakeLists.txt 文件
1
2
3
4
5
6
7
8
# 指定CMake版本号
cmake_minimum_required(VERSION 3.15)

# 指定项目名称
project(xlog)

# 编译生成静态库
add_library(xlog STATIC xlog.cpp xlog.h)
  • 编译项目生成静态库
1
2
3
4
5
6
7
8
# 进入源码目录
cd xlog

# 配置项目,生成构建文件(例如 Makefile 或 Ninja 文件)
cmake -S . -B build

# 编译项目,生成静态库
cmake --build build
  • 编译结果
系统平台编译生成的静态库文件
Windowsxlog.lib
Linuxlibxlog.a
MacOSlibxlog.a

CMake 链接静态库

通过上面的步骤使用 CMake 编译生成 C/C++ 项目的静态库后,这里将演示如何使用 CMake 链接 C/C++ 项目的静态库,最终编译生成可执行文件。

  • 项目的目录结构
1
2
3
4
5
6
7
8
cmake_lib
├── test_xlog
│   ├── CMakeLists.txt
│   └── test_xlog.cpp
└── xlog
├── CMakeLists.txt
├── xlog.cpp
└── xlog.h
  • xlog 目录下的 xlog.h 源文件
1
2
3
4
5
6
7
8
9
10
11
#ifndef XLOG_H
#define XLOG_H

class XLog {

public:
XLog();

};

#endif
  • xlog 目录下的 xlog.cpp 源文件
1
2
3
4
5
6
7
8
#include <iostream>
#include "xlog.h"

using namespace std;

XLog::XLog() {
cout << "Create XLog Instance" << endl;
}
  • xlog 目录下的 CMakeLists.txt 文件
1
2
3
4
5
6
7
8
# 指定CMake版本号
cmake_minimum_required(VERSION 3.15)

# 指定项目名称
project(xlog)

# 编译生成静态库
add_library(xlog STATIC xlog.cpp xlog.h)
  • test_xlog 目录下的 test_xlog.cpp 源文件
1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include "xlog.h"

using namespace std;

int main() {
XLog log;
cout << "test xlog" << endl;
return 0;
}
  • test_xlog 目录下的 CMakeLists.txt 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 指定CMake的版本号
cmake_minimum_required(VERSION 3.15)

# 指定项目名称
project(test_log)

# 指定头文件的搜索路径
include_directories("../xlog")

# 指定静态库的搜索路径
link_directories("../xlog/build")

# 编译生成可执行文件
add_executable(test_xlog test_xlog.cpp)

# 指定需要链接的静态库
target_link_libraries(test_xlog xlog)
  • 编译项目生成可执行文件
1
2
3
4
5
6
7
8
# 进入源码目录
cd test_xlog

# 配置项目,生成构建文件(例如 Makefile 或 Ninja 文件)
cmake -S . -B build

# 编译项目,生成可执行文件
cmake --build build
  • 编译结果
系统平台编译生成的可执行文件
Windowstest_xlog.exe
Linuxtest_xlog
MacOStest_xlog

CMake 编译与链接动态库

代码下载

完整的 CMake 案例代码可以从 这里 下载得到。

CMake 编译动态库

这里将演示如何使用 CMake 编译生成 C/C++ 项目的动态库。

  • 项目的目录结构
1
2
3
4
5
cmake_dylib
├── CMakeLists.txt
└── xlog
├── xlog.cpp
└── xlog.h
  • xlog 目录下的 xlog.h 源文件
1
2
3
4
5
6
7
8
9
10
11
#ifndef XLOG_H
#define XLOG_H

class XLog {

public:
XLog();

};

#endif
  • xlog 目录下的 xlog.cpp 源文件
1
2
3
4
5
6
7
8
#include <iostream>
#include "xlog.h"

using namespace std;

XLog::XLog() {
cout << "Create XLog Instance" << endl;
}
  • cmake_dylib 目录下的 CMakeLists.txt 文件
1
2
3
4
5
6
7
8
# 指定 CMake 的版本号
cmake_minimum_required(VERSION 3.15)

# 指定项目名称
project(xlog)

# 编译生成动态库
add_library(xlog SHARED xlog/xlog.cpp)
  • 编译项目生成动态库
1
2
3
4
5
6
7
8
# 进入源码目录
cd cmake_dylib

# 配置项目,生成构建文件(例如 Makefile 或 Ninja 文件)
cmake -S . -B build

# 编译项目,生成动态库
cmake --build build
  • 编译结果
系统平台编译生成的动态库文件
Windowsxlog.lib, xlog.dll
Linuxlibxlog.so
MacOSlibxlog.dylib

CMake 链接动态库

通过上面的步骤使用 CMake 编译生成 C/C++ 项目的动态库后,这里将演示如何使用 CMake 链接 C/C++ 项目的动态库,最终编译生成可执行文件。

  • 项目的目录结构
1
2
3
4
5
6
7
cmake_dylib
├── CMakeLists.txt
├── test_xlog
│   └── test_xlog.cpp
└── xlog
├── xlog.cpp
└── xlog.h
  • xlog 目录下的 xlog.h 源文件
1
2
3
4
5
6
7
8
9
10
11
#ifndef XLOG_H
#define XLOG_H

class XLog {

public:
XLog();

};

#endif
  • xlog 目录下的 xlog.cpp 源文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef XLOG_H
#define XLOG_H

// 在 Windows 平台编译动态库时,__declspec(dllexport) 用于将 XLog 类的函数导出到动态库文件中
// 在 Windows 平台编译动态库时,__declspec(dllimport) 用于从动态库文件中导入 XLog 类的函数

#ifndef _WIN32
#define XCPP_API
#else
#ifdef xlog_EXPORTS
#define XCPP_API __declspec(dllexport) // 动态库项目的导出
#else
#define XCPP_API __declspec(dllimport) // 动态库项目的导入
#endif
#endif

class XCPP_API XLog {

public:
XLog();

};

#endif
  • test_xlog 目录下的 test_xlog.cpp 源文件
1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include "xlog.h"

using namespace std;

int main() {
XLog log;
cout << "test xlog" << endl;
return 0;
}
  • cmake_dylib 目录下的 CMakeLists.txt 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 指定 CMake 的版本号
cmake_minimum_required(VERSION 3.15)

# 指定项目名称
project(xlog)

# 指定头文件的搜索路径
include_directories("xlog")

# 编译生成动态库,通常会自带预处理变量 xlog_EXPORTS
add_library(xlog SHARED xlog/xlog.cpp)

# 编译生成可执行文件
add_executable(test_xlog test_xlog/test_xlog.cpp xlog)

# 指定需要链接的动态库
target_link_libraries(test_xlog xlog)

提示

CMake 在编译的时候,会将动态库的文件路径写死,如果需要使用相对路径,可以使用类似 -Wl, -rpath, /opt/mker/poco/lib 这样的参数来实现。

  • 编译项目生成可执行文件
1
2
3
4
5
6
7
8
# 进入源码目录
cd cmake_dylib

# 配置项目,生成构建文件(例如 Makefile 或 Ninja 文件)
cmake -S . -B build

# 编译项目,生成可执行文件
cmake --build build
  • 编译结果
系统平台编译生成的可执行文件
Windowstest_xlog.exe
Linuxtest_xlog
MacOStest_xlog