😈「CSDN主页」:传送门
😈「Bilibil首页」:传送门
😈「本文的内容」:CMake入门教程
😈「动动你的小手」:点赞👍收藏⭐️评论📝
文章目录
- 1. 基本用法
- 2.STATIC 、SHARED 、 MODULE基本用法
- 2.1 创建静态库
- 2.2 创建共享库
- 2.3 创建模块库
- 3. 设置库的属性
- 4. 安装库
- 5. 链接其他库
- 6. 使用别名
- 7. 管理大型项目
- 8.代码示例
- 8.1完整示例MODULE
- 8.2完整示例SHARED
- 8.3完整示例STATIC
- 8.4例子分析STATIC 、SHARED、MODULE分析
- 9. 结论
在CMake中,
add_library
命令是用于定义库(library)的关键命令。库可以是静态的(static),动态的(shared),或者是模块库。本节教程将深入探讨如何在CMake中使用add_library
命令来创建不同类型的库。
1. 基本用法
add_library
命令用于创建一个库。基本语法如下:
add_library(<name> [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1 [source2 ...])
<name>
:库的名称。STATIC
、SHARED
、MODULE
:指定库的类型。如果不指定,默认创建的是静态库(STATIC)。EXCLUDE_FROM_ALL
:如果设置,这个库不会被默认构建,除非有其他目标依赖它。source1 [source2 ...]
:库的源文件。
2.STATIC 、SHARED 、 MODULE基本用法
2.1 创建静态库
静态库(.a
或.lib
文件)在编译时被完全复制到最终的可执行文件中。
add_library(my_static_lib STATIC source1.cpp source2.cpp)
2.2 创建共享库
共享库(.so
、.dll
或.dylib
)在运行时被动态加载。
add_library(my_shared_lib SHARED source1.cpp source2.cpp)
2.3 创建模块库
模块库(通常用于插件)在运行时可以被动态加载,但不会被链接到其他目标中。
add_library(my_module_lib MODULE source1.cpp source2.cpp)
3. 设置库的属性
使用target_*
命令设置库的属性,例如包含目录、编译定义等。
target_include_directories(my_shared_lib PUBLIC include)
target_compile_definitions(my_shared_lib PRIVATE MY_SHARED_LIB_BUILD)
4. 安装库
使用install
命令来指定库安装的规则。
install(TARGETS my_shared_lib DESTINATION lib)
5. 链接其他库
使用target_link_libraries
将其他库链接到您创建的库。
target_link_libraries(my_shared_lib another_library)
6. 使用别名
为库创建别名,使其可以在项目中以一致的方式被引用。
add_library(my_lib_alias ALIAS my_shared_lib)
7. 管理大型项目
对于大型项目,合理组织多个库文件可以提高项目的模块化和可维护性。考虑将功能相关的类和函数分组到不同的库中。
8.代码示例
8.1完整示例MODULE
了创建一个跨平台的示例,我们需要编写一个可以在Windows和类Unix系统(如Linux和macOS)上运行的程序。为此,我们将使用预处理器指令来区分平台并使用相应的动态加载库API。下面的示例展示了如何在CMake中创建一个模块库,并在主程序中根据操作系统动态加载这个库。
- 项目结构
cssCopy codeMyProject/
├── CMakeLists.txt
└── src/
├── Plugin.cpp
└── main.cpp
- Plugin.cpp
模块库的实现文件,位于
src/
目录下。
cppCopy code// Plugin.cpp
#include <iostream>
extern "C" void loadPlugin() {
std::cout << "Plugin Loaded!" << std::endl;
}
- main.cpp
主程序的实现文件,用于动态加载模块库。
// main.cpp
#include <iostream>
#if defined(_WIN32)
#include <windows.h>
#else
#include <dlfcn.h>
#endif
int main() {
#if defined(_WIN32)
HMODULE hModule = LoadLibrary(TEXT("MyPlugin.dll"));
if (!hModule) {
std::cerr << "Cannot load library: " << GetLastError() << '\n';
return 1;
}
typedef void (*Func)();
Func loadPlugin = (Func) GetProcAddress(hModule, "loadPlugin");
#else
void* handle = dlopen("./libMyPlugin.so", RTLD_LAZY);
if (!handle) {
std::cerr << "Cannot load library: " << dlerror() << '\n';
return 1;
}
typedef void (*Func)();
dlerror(); // 清除现有的错误
Func loadPlugin = (Func) dlsym(handle, "loadPlugin");
const char* dlsym_error = dlerror();
if (dlsym_error) {
std::cerr << "Cannot load symbol 'loadPlugin': " << dlsym_error << '\n';
dlclose(handle);
return 1;
}
#endif
// 使用库
loadPlugin();
// 关闭库
#if defined(_WIN32)
FreeLibrary(hModule);
#else
dlclose(handle);
#endif
return 0;
}
- CMakeLists.txt
CMake的配置文件,用于构建整个项目。
cmake_minimum_required(VERSION 3.10)
project(MyPluginProject)
# 创建模块库
add_library(MyPlugin MODULE src/Plugin.cpp)
# 创建可执行文件
add_executable(MyExecutable src/main.cpp)
# 设置输出目录(可选)
set_target_properties(MyPlugin PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins"
)
在这个示例中,我们首先创建了一个名为MyPlugin
的模块库,然后创建了一个名为MyExecutable
的可执行文件。main.cpp
中的代码根据编译平台选择正确的动态加载方法。
这个跨平台的示例演示了如何在CMake中创建和使用模块库,并展示了如何在Windows和类Unix系统上动态加载和使用这种类型的库
8.2完整示例SHARED
- 项目结构
设想我们的项目结构如下:
cssCopy codeMyProject/
├── CMakeLists.txt
├── include/
│ └── MySharedLib.h
└── src/
├── MySharedLib.cpp
└── main.cpp
- MySharedLib.h
这是共享库的头文件,位于include/
目录下。
cppCopy code// MySharedLib.h
#ifndef MYSHAREDLIB_H
#define MYSHAREDLIB_H
void printHelloShared();
#endif // MYSHAREDLIB_H
- MySharedLib.cpp
这是共享库的实现文件,位于
src/
目录下。
cppCopy code// MySharedLib.cpp
#include "MySharedLib.h"
#include <iostream>
void printHelloShared() {
std::cout << "Hello from MySharedLib!" << std::endl;
}
- main.cpp
这是主程序的实现文件,也位于
src/
目录下。
cppCopy code// main.cpp
#include "MySharedLib.h"
int main() {
printHelloShared();
return 0;
}
- CMakeLists.txt
这是CMake的配置文件,用于构建整个项目。
cmakeCopy codecmake_minimum_required(VERSION 3.10)
# 设置项目名称
project(MyProject)
# 指定包含目录
include_directories(include)
# 添加共享库"MySharedLib"
add_library(MySharedLib SHARED src/MySharedLib.cpp)
# 创建可执行文件
add_executable(MyExecutable src/main.cpp)
# 链接共享库到可执行文件
target_link_libraries(MyExecutable MySharedLib)
在这个例子中,我们首先设置了项目名称和包含目录。接着,我们使用add_library
命令添加了一个名为MySharedLib
的共享库,并指定了它的源文件。然后,我们用add_executable
命令创建了一个名为MyExecutable
的可执行文件,并使用target_link_libraries
命令将MySharedLib
共享库链接到这个可执行文件上。
这个完整的示例展示了如何在CMake中创建和使用共享库,以及如何在应用程序中链接和使用这种类型的库。
8.3完整示例STATIC
- 项目结构
MyProject/
├── CMakeLists.txt
├── include/
│ └── MyStaticLib.h
└── src/
├── MyStaticLib.cpp
└── main.cpp
- MyStaticLib.h
这是静态库的头文件,位于include/
目录下。
cppCopy code// MyStaticLib.h
#ifndef MYSTATICLIB_H
#define MYSTATICLIB_H
void printHelloStatic();
#endif // MYSTATICLIB_H
- MyStaticLib.cpp
这是静态库的实现文件,位于src/
目录下。
cppCopy code// MyStaticLib.cpp
#include "MyStaticLib.h"
#include <iostream>
void printHelloStatic() {
std::cout << "Hello from MyStaticLib!" << std::endl;
}
- main.cpp
这是主程序的实现文件,也位于src/
目录下。
cppCopy code// main.cpp
#include "MyStaticLib.h"
int main() {
printHelloStatic();
return 0;
}
- CMakeLists.txt
这是CMake的配置文件,用于构建整个项目。
codecmake_minimum_required(VERSION 3.10)
# 设置项目名称
project(MyProject)
# 指定包含目录
include_directories(include)
# 添加静态库"MyStaticLib"
add_library(MyStaticLib STATIC src/MyStaticLib.cpp)
# 创建可执行文件
add_executable(MyExecutable src/main.cpp)
# 链接静态库到可执行文件
target_link_libraries(MyExecutable MyStaticLib)
在这个示例中,我们首先设置了项目名称和包含目录。接着,我们使用add_library
命令添加了一个名为MyStaticLib
的静态库,并指定了它的源文件。然后,我们用add_executable
命令创建了一个名为MyExecutable
的可执行文件,并使用target_link_libraries
命令将MyStaticLib
静态库链接到这个可执行文件上。
这个完整的示例展示了如何在CMake中创建和使用静态库,以及如何在应用程序中链接和使用这种类型的库。
8.4例子分析STATIC 、SHARED、MODULE分析
创建和使用不同类型的库(静态库、共享库、模块库)是CMake中的一个核心概念。以下是对之前提到的三种类型库的创建和使用的总结:
1. 静态库(STATIC)
- 定义:静态库是编译时链接到可执行文件中的库,不需要在运行时动态加载。
- 优点:简化部署(不需要确保共享库在运行时可用),提高运行时性能(减少动态链接的开销)。
- 使用场景:适用于不需要共享代码的情况,以及对性能有较高要求的场景。
2. 共享库(SHARED)
- 定义:共享库是在运行时被动态加载的库,允许代码在多个程序之间共享。
- 优点:节省内存(同一共享库的单个副本可被多个程序使用),易于更新(更新共享库而无需重新编译使用它的程序)。
- 使用场景:适用于需要跨多个程序共享代码的场景。
3. 模块库(MODULE)
- 定义:模块库通常用于实现插件或动态加载模块,它们在构建时不链接到其他目标,但可以在运行时动态加载。
- 优点:灵活性高(可随需加载或卸载),有助于减小应用程序的初始内存占用。
- 使用场景:适用于插件系统或需要可拓展性的应用程序。
在实际项目中,选择哪种类型的库取决于项目的需求和设计目标。静态库适合内部紧密耦合的组件,共享库适合于需要跨多个应用共享的代码,而模块库则是为动态拓展性而设计。理解这些不同类型的库以及它们的用途和优势,可以帮助开发者更有效地构建和管理CMake项目
9. 结论
通过有效地使用add_library
,您可以在CMake项目中灵活地创建和管理各种类型的库。这不仅有助于提高项目的结构清晰度,还可以在不同的场景下为您的应用或库提供适当的链接和加载机制。