CMake入门教程【核心篇】添加库(add_library)

在这里插入图片描述

😈「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>:库的名称。
  • STATICSHAREDMODULE:指定库的类型。如果不指定,默认创建的是静态库(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>
#endifint 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");#elsevoid* 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);#elsedlclose(handle);#endifreturn 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 PROPERTIESLIBRARY_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_Hvoid 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_Hvoid 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项目中灵活地创建和管理各种类型的库。这不仅有助于提高项目的结构清晰度,还可以在不同的场景下为您的应用或库提供适当的链接和加载机制。

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

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

相关文章

Harmony 开始支持 Flutter ,聊聊 Harmony 和 Flutter 之间的因果

原创作者&#xff1a;恋猫de小郭 相信大家都已经听说过&#xff0c;明年的 Harmony Next 版本将正式剥离 AOSP 支持 &#xff0c;基于这个话题我已经做过一期问题汇总 &#xff0c;当时在 现有 App 如何兼容 Harmony Next 问题上提到过&#xff1a; 华为内部也主导适配目前的主…

力扣题:高精度运算-1.2

力扣题-1.2 [力扣刷题攻略] Re&#xff1a;从零开始的力扣刷题生活 力扣题1&#xff1a;415. 字符串相加 解题思想&#xff1a;从后往前遍历两个字符串,然后进行相加即可 class Solution(object):def addStrings(self, num1, num2):""":type num1: str:type …

uni-app 前后端调用实例 基于Springboot 详情页实现

锋哥原创的uni-app视频教程&#xff1a; 2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中..._哔哩哔哩_bilibili2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中...共计23条视频&#xff0c;包括&#xff1a;第1讲 uni…

计算机基础面试题 |08.精选计算机基础面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

Linux——进程初识(二)

1. 对当前目录创建文件的理解 我们知道在创建一个文件时&#xff0c;它会被默认创建到当前目录下&#xff0c;那么它是如何知道当前目录的呢&#xff1f; 对于下面这样一段代码 #include <stdio.h> #include <unistd.h>int main() {fopen("tmp.txt", …

stable diffusion 人物高级提示词(一)头部篇

一、女生发型 prompt描述推荐用法Long hair长发一定不要和 high ponytail 一同使用Short hair短发-Curly hair卷发-Straight hair直发-Ponytail马尾high ponytail 高马尾&#xff0c;一定不要和 long hair一起使用&#xff0c;会冲突Pigtails2条辫子-Braid辫子只写braid也会生…

PNG图片导入Abaqus建模:Abaqus Image To Part 2D插件

插件介绍 Abaqus Image To Part 2D - AbyssFish 插件可将图像导入Abaqus内并通过对网格单元集进行材料指定&#xff0c;实现基于图像的模型部件生成。 插件支持JPEG、JPG、PNG、GIF、TIFF、BMP、PCX、ICO等多种图像格式&#xff0c;兼容彩图、灰度图、二值图像等类型&#x…

2_并发编程同步锁(synchronized)

并发编程带来的安全性同步锁(synchronized) 1.他的背景 当多个线程同时访问&#xff0c;公共共享资源的时候&#xff0c;这时候就会出现线程安全&#xff0c;代码如&#xff1a; public class AtomicDemo {int i0;//排他锁、互斥锁public void incr(){ //synchronizedi; …

基于SSM的滁艺咖啡在线销售系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

opencv入门到精通——Canny边缘检测

目录 理论 OpenCV中的Canny Edge检测 附加资源 在本章中&#xff0c;我们将学习 Canny边缘检测的概念 OpenCV函数: cv.Canny() 理论 Canny Edge Detection是一种流行的边缘检测算法。它由John F. Canny发明 1.这是一个多阶段算法&#xff0c;我们将经历每个阶段。 2.降…

六、基于Flask、Flasgger、marshmallow的开发调试

基于Flask、Flasgger、marshmallow的开发调试 问题描述调试方法一调试方法二调试方法三 问题描述 现在有一个传入传出为json格式文件的&#xff0c;Flask-restful开发的程序&#xff0c;需要解决如何调试的问题。 #!/usr/bin/python3 # -*- coding: utf-8 -*- # Project :…

数据结构——二叉树四种遍历的实现

目录 一、树的概念 1、树的定义 1&#xff09;树 2&#xff09;空树 3&#xff09;子树 2、结点的定义 1&#xff09;根结点 2&#xff09;叶子结点 3&#xff09;内部结点 3、结点间关系 1&#xff09;孩子结点 2&#xff09;父结点 3&#xff09;兄弟结点 4、树…