【CMake】(7)生成库

静态库生成

假设你的项目结构如下:

css 复制代码
MyProject/
├── CMakeLists.txt
├── include/
│   └── head.h
└── src/
    ├── add.cpp
    ├── div.cpp
    ├── mult.cpp
    └── sub.cpp

你想要将src目录下的源文件编译成一个名为calc的静态库,并将该库的头文件放在include目录下。

c 复制代码
#ifndef _HEAD_H_
#define _HEAD_H_
// 加法
int add(int a, int b);
// 减法
int subtract(int a, int b);
// 乘法
int multiply(int a, int b);
// 除法
double divide(int a, int b);

#endif // _HEAD_H_

CMakeLists.txt文件配置

为了生成静态库,你需要在CMakeLists.txt文件中指定库的名称、类型(静态或动态),以及包含在库中的源文件。

cmake 复制代码
cmake_minimum_required(VERSION 3.0)
project(CALC)

# 包含头文件目录
include_directories(${PROJECT_SOURCE_DIR}/include)

# 搜索当前目录下的所有源文件,并将名称保存到 SRC_LIST 变量
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")

# 添加静态库目标
add_library(calc STATIC ${SRC_LIST})

这里的add_library命令用于生成静态库。calc是库的名称,STATIC指明这是一个静态库,${SRC_LIST}包含了要编译进库的所有源文件。

静态库命名约定

  • Linux 上,静态库的命名约定是lib前缀加上库的名称,然后是.a后缀。例如,上述示例中的库将会被命名为libcalc.a
  • Windows 上,静态库通常有.lib后缀,但没有lib前缀,因此上述库可能会被命名为calc.lib

使用静态库

一旦静态库被创建,它就可以被其他项目通过target_link_libraries命令链接使用了。如果静态库和主项目在同一个CMake项目中,可以直接链接;如果不在,可能需要使用find_library或者手动设置静态库的路径。

vs使用方法

1. 将静态库添加到项目中

首先,你需要将静态库文件(.lib)复制到你的项目目录中,或者至少保证Visual Studio能够找到它。 ![[1707799640675.jpg]]

2. 配置项目以链接静态库

接下来,需要配置项目来链接这个静态库。方法如下:

  • 打开Visual Studio,右键点击解决方案资源管理器中的项目名,选择"属性"。
  • 点击"C/C++"->"常规",在"附加包含目录"字段添加头文件的地址。 ![[Pasted image 20240213125305.png]]
  • 在弹出的属性页中,选择"链接器"->"输入",在"附加依赖项"字段中输入你的静态库文件名,比如calc.lib。如果有多个库,用分号隔开。 ![[Pasted image 20240213125356.png]]
  • 如果你的静态库不在项目目录中,还需要在"链接器"->"常规"下的"附加库目录"中添加静态库所在的目录路径。 ![[Pasted image 20240213125425.png]]
3. 包含头文件并调用
c 复制代码
#include <stdio.h>
#include "head.h"
int main()
{
    int a = 20;
    int b = 12;
    printf("a = %d, b = %d\n", a, b);
    printf("a + b = %d\n", add(a, b));
    printf("a - b = %d\n", subtract(a, b));
    printf("a * b = %d\n", multiply(a, b));
    printf("a / b = %f\n", divide(a, b));
    return 0;
}

注意

  • 确保在Release和Debug模式下都配置了静态库,有时你可能需要为不同的编译模式链接不同版本的库。
  • 如果在链接时出现找不到符号的错误,可能是因为没有正确配置静态库,或者静态库和你的项目使用了不同的运行时库设置(比如,一个使用了多线程DLL,另一个没有)。确保库和你的项目使用了相同的设置。

动态库生成

动态库在程序运行时被加载,可以被多个程序共享,这样可以节省内存,同时也便于更新和维护。

步骤 1: 准备源代码

首先,你需要有源代码文件。假设你的项目结构如下所示,其中包括多个.c文件(或.c)和头文件:

css 复制代码
MyProject/
├── include/
│   └── head.h
└── src/
    ├── add.c
    ├── div.c
    ├── mult.c
    └── sub.c

head.h文件可能看起来像这样:

c 复制代码
#ifndef _HEAD_H_
#define _HEAD_H_

#ifdef _WIN32
#ifdef CALC_EXPORTING
#define CALC_API __declspec(dllexport)
#else
#define CALC_API __declspec(dllimport)
#endif
#else
#define CALC_API
#endif

#ifdef __cplusplus
extern "C" {
#endif

// 加法
CALC_API int add(int a, int b);
// 减法
CALC_API int subtract(int a, int b);
// 乘法
CALC_API int multiply(int a, int b);
// 除法
CALC_API double divide(int a, int b);
  
#ifdef __cplusplus
}
#endif

#endif // _HEAD_H_

步骤 2: 编写CMake配置

在项目的根目录下创建一个CMakeLists.txt文件,并填写以下内容:

cmake 复制代码
cmake_minimum_required(VERSION 3.0)

project(CALC)

# 设置C++标准
set(CMAKE_CXX_STANDARD 11)

# 设置可执行文件的输出路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin/)

# 添加头文件搜索路径
include_directories(${PROJECT_SOURCE_DIR}/include)

# 查找当前目录下的所有源文件,并将名称保存到 SRC_LIST 变量
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
# 添加一个可执行文件
#add_executable(app  ${SRC_LIST})
# 添加静态库目标
add_library(calc SHARED ${SRC_LIST})

target_compile_definitions(calc PRIVATE CALC_EXPORTING)

步骤 3: 构建动态库

在项目根目录下,打开终端或命令提示符,运行以下命令来创建构建目录(如果还没有)并使用CMake配置项目:

bash 复制代码
mkdir build
cmake -S . -B build
cmake --build build

这会在指定的库输出路径(这里是项目根目录下的lib文件夹)生成动态库文件libcalc.so(在Linux上)或calc.dll(在Windows上)和对应平台的静态库文件,这两个文件都是需要的。

  • 动态库的名字和格式取决于你的操作系统。在Linux和UNIX系统上,动态库通常有.so扩展名,在Windows上则是.dll
  • 使用动态库时,确保在运行时库路径可找到,否则程序可能无法启动。在Linux上,你可以使用LD_LIBRARY_PATH环境变量来指定动态库的搜索路径。
  • 动态库的一个优点是可以在不重新编译程序的情况下更新库。然而,要确保新版本的库是向后兼容的,以避免运行时错误。

使用动态库

vs使用方法

同静态库基本一致,lib和dll文件都需要,而且注意默认情况下要将dll文件放置到项目根目录。

输出路径

可以通过设置特定的变量来指定库文件(无论是动态库还是静态库)和可执行文件的输出路径。

方式1 - 主要适用于动态库和可执行文件

通过设置EXECUTABLE_OUTPUT_PATH变量,你可以指定可执行文件和具有执行权限的动态库文件的输出路径。这种方式主要适用于动态库和可执行文件。

cmake 复制代码
cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")

# 设置动态库和可执行文件生成路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

# 生成动态库
add_library(calc SHARED ${SRC_LIST})

方式2 - 适用于动态库和静态库

使用LIBRARY_OUTPUT_PATH变量,可以同时指定动态库和静态库的输出路径。这个变量对于动态库和静态库都适用,但不影响可执行文件的输出路径。

cmake 复制代码
cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")

# 设置动态库/静态库生成路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

# 生成动态库
# add_library(calc SHARED ${SRC_LIST})
# 生成静态库
add_library(calc STATIC ${SRC_LIST})

注意事项

  • EXECUTABLE_OUTPUT_PATHLIBRARY_OUTPUT_PATH 变量指定的是目标文件生成时的路径,而不影响安装(make install)时的路径。安装路径需要通过INSTALL(TARGETS ...)命令单独指定。
  • 如果不指定输出路径,CMake默认会将目标文件放置在构建目录的某个子目录下,这个子目录的结构通常会反映源代码目录的结构。
  • 在多目标项目中,也可以对每个目标单独设置输出路径,通过为目标设置RUNTIME_OUTPUT_DIRECTORYLIBRARY_OUTPUT_DIRECTORYARCHIVE_OUTPUT_DIRECTORY属性实现。
相关推荐
Coding-Prince2 天前
cmake报错The link interface of target “gRPC::grpc“ contains: OpenSSL::SSL 解决
ssl·cmake
凌云行者6 天前
OpenGL入门005——使用Shader类管理着色器
c++·cmake·opengl
凌云行者6 天前
OpenGL入门006——着色器在纹理混合中的应用
c++·cmake·opengl
赵民勇9 天前
cmake中execute_process详解
cmake
凌云行者9 天前
OpenGL入门004——使用EBO绘制矩形
c++·cmake·opengl
长弓聊编程11 天前
应该怎么理解CMakeLists.txt中一些指令的INTERFACE、PUBLIC和PRIVATE参数
cmake
凌云行者12 天前
OpenGL入门003——使用Factory设计模式简化渲染流程
c++·cmake·opengl
凌云行者12 天前
OpenGL入门002——顶点着色器和片段着色器
c++·cmake·opengl
梦起丶13 天前
CMake 生成器表达式介绍
c++·cmake
梦起丶15 天前
CMake 生成器表达式---条件表达式和逻辑运算符
c++·cmake