cmake怎么讲动态库和静态库一起生成
时间: 2025-07-28 19:56:39 AIGC 浏览: 18
<think>我们被要求用中文回答,并且遵循特定的LaTeX格式要求。用户的问题是关于在CMake中同时生成动态库和静态库。
根据引用[2]和引用[3],我们可以知道同时生成动态库和静态库的方法,并且要求它们具有相同的名称(除了扩展名不同)。在CMake中,由于不能有两个同名的目标,所以我们需要一些技巧。
引用[2]中提到,我们可以先编译一个静态库目标(比如命名为hello_static),然后通过设置属性将其输出名称改为“hello”,这样就会生成libhello.a。同时,动态库目标可以直接命名为hello,生成libhello.so。
引用[3]则提供了一种避免重复编译的方法:使用对象库(Object Library)来避免重复编译源文件。具体做法是,先创建一个共享库目标(动态库),然后使用`add_library`命令创建静态库目标时,使用`$<TARGET_OBJECTS:...>`来引用共享库目标的对象文件,这样源文件就不会被重复编译。
但是,引用[3]中的方法实际上创建了两个库,动态库和静态库,静态库使用了动态库的目标对象。然而,这种方法可能并不总是可行,因为动态库和静态库的编译选项(如位置无关代码)可能不同。
另一种更常见的方法(如引用[2])是分别创建两个目标(动态库和静态库),并分别设置它们的输出名称。但是这样会导致源文件被编译两次(一次用于动态库,一次用于静态库)。如果源文件很多,这会增加编译时间。
因此,我们需要权衡。如果动态库和静态库的编译选项相同(比如都要求位置无关代码),那么我们可以使用对象库来避免重复编译。否则,我们只能分别编译。
下面我们提供两种方法:
方法1:分别编译动态库和静态库,并设置相同的输出名称(除了扩展名)
方法2:使用对象库来避免重复编译(要求编译选项一致)
由于用户没有特别说明编译选项,我们提供两种方法,并说明适用情况。
注意:在CMake中,静态库和动态库的默认输出名称就是目标名称,所以我们通过设置静态库的输出名称来去掉目标名称中用于区分的后缀(比如_static),使得静态库和动态库的输出名称相同。
下面我们具体说明:
方法1:分别编译(可能重复编译,但更通用)
步骤:
1. 添加动态库目标,使用`add_library`并指定`SHARED`。
2. 添加静态库目标,使用`add_library`并指定`STATIC`,注意目标名称不能和动态库相同。
3. 为静态库目标设置属性,将其输出名称改为和动态库目标相同的名称(不包括扩展名)。
示例CMakeLists.txt:
```cmake
# 设置源文件
set(SRC_FILES src1.cpp src2.cpp)
# 添加动态库目标,目标名为mylib_shared,输出文件为libmylib.so(在Linux上)
add_library(mylib_shared SHARED ${SRC_FILES})
# 添加静态库目标,目标名为mylib_static,输出文件为libmylib_static.a(默认)
add_library(mylib_static STATIC ${SRC_FILES})
# 将静态库的输出名称改为mylib(去掉_static后缀),这样输出文件就是libmylib.a
set_target_properties(mylib_static PROPERTIES OUTPUT_NAME "mylib")
# 对于Windows,可能还需要设置不同的后缀,但这里我们主要考虑Linux
```
这样,在构建时,我们会得到两个库文件:libmylib.so(动态库)和libmylib.a(静态库)。
但是,这种方法会编译两次源文件:一次用于动态库(带有-fPIC选项),一次用于静态库(默认不带-fPIC)。如果希望静态库也使用位置无关代码(PIC),可以单独为静态库目标设置PIC属性:
```cmake
set_target_properties(mylib_static PROPERTIES POSITION_INDEPENDENT_CODE ON)
```
方法2:使用对象库避免重复编译(要求两个库的编译选项一致)
步骤:
1. 创建一个对象库,将源文件编译成对象文件(不打包)。
2. 从对象库创建动态库和静态库。
示例:
```cmake
# 设置源文件
set(SRC_FILES src1.cpp src2.cpp)
# 创建对象库(不指定类型,只编译对象文件)
add_library(myobj OBJECT ${SRC_FILES})
# 从对象库创建动态库
add_library(mylib_shared SHARED $<TARGET_OBJECTS:myobj>)
# 从对象库创建静态库
add_library(mylib_static STATIC $<TARGET_OBJECTS:myobj>)
# 设置静态库的输出名称与动态库相同(不包括扩展名)
set_target_properties(mylib_static PROPERTIES OUTPUT_NAME "mylib")
```
注意:这种方法要求动态库和静态库的编译选项(特别是位置无关代码)必须相同。因为对象库的编译选项是固定的。通常,对于动态库,我们要求位置无关代码(PIC),而静态库不一定需要。但是,如果我们希望静态库也可以被用于生成动态库(例如,被其他动态库链接),那么静态库也需要PIC。所以,我们可以在对象库上设置PIC:
```cmake
set_target_properties(myobj PROPERTIES POSITION_INDEPENDENT_CODE ON)
```
这样,两个库就都使用了PIC。
但是,方法2有一个限制:对象库的源文件不能有特定于目标(动态库或静态库)的编译选项。如果两个库需要不同的编译选项,则不能使用对象库。
总结:
在CMake中同时生成同名的动态库和静态库,可以通过分别创建两个目标并设置静态库的输出名称来实现(方法1)。如果希望避免重复编译且两个库的编译选项一致,可以使用对象库(方法2)。
根据引用[2]和[3],我们提供了这两种方法。
注意:在Windows平台上,动态库和静态库的命名规则不同(动态库是.dll和.lib,静态库是.lib),所以同时生成同名库可能会冲突(因为扩展名不同,但基名相同,在同一个目录下,静态库会覆盖动态库的导入库)。因此,在Windows上通常需要将静态库和动态库放在不同的目录,或者使用不同的名称。但在Linux上,动态库是.so,静态库是.a,所以不会冲突。
因此,在实际项目中,我们可能还需要考虑安装目录的组织。
下面是一个完整的示例(Linux环境下),使用方法1:
```cmake
cmake_minimum_required(VERSION 3.10)
project(mylib)
set(SRC_FILES src1.cpp src2.cpp)
# 动态库
add_library(mylib_shared SHARED ${SRC_FILES})
# 设置动态库的属性,例如VERSION等
set_target_properties(mylib_shared PROPERTIES OUTPUT_NAME "mylib")
# 静态库
add_library(mylib_static STATIC ${SRC_FILES})
# 设置静态库的输出名称为mylib,这样生成的文件就是libmylib.a
set_target_properties(mylib_static PROPERTIES OUTPUT_NAME "mylib")
# 安装
install(TARGETS mylib_shared mylib_static
ARCHIVE DESTINATION lib # 静态库安装到lib目录
LIBRARY DESTINATION lib) # 动态库安装到lib目录
```
注意:在同一个CMake工程中,两个目标不能同名,所以我们用mylib_shared和mylib_static来区分,但是通过设置OUTPUT_NAME,使得它们的输出文件都是libmylib(扩展名不同)。
另外,在安装时,我们同时安装两个目标。由于在Linux下,动态库和静态库的扩展名不同,所以不会冲突。
但是,在Windows上,动态库的导入库(.lib)和静态库(.lib)会冲突,因为它们的扩展名相同。因此,在Windows上,我们通常需要将静态库和动态库的导入库放在不同的目录,或者使用不同的名称(例如,静态库加上_static后缀)。这里我们主要讨论Linux,所以不展开。
因此,用户可以根据自己的平台和需求选择合适的方法。
最后,我们生成相关问题:
§§相关问题§§
1. 在CMake中如何为动态库和静态库设置不同的编译选项?
2. 在Windows平台上如何同时生成动态库和静态库并避免文件名冲突?
3. CMake对象库(Object Library)还有哪些应用场景?</think>### 如何在CMake中同时生成动态库和静态库
在CMake中同时生成名称相同的动态库(.so/.dll)和静态库(.a/.lib)需要特殊处理,因为CMake不允许目标重名。以下是两种常用方法:
#### 方法1:分别编译 + 重命名输出(推荐)
```cmake
# 设置公共源文件
set(SRC_FILES src1.cpp src2.cpp)
# 动态库目标 (默认输出 libhello.so)
add_library(hello_shared SHARED ${SRC_FILES})
# 静态库目标 (默认输出 libhello_static.a)
add_library(hello_static STATIC ${SRC_FILES})
# 重命名静态库输出为 libhello.a
set_target_properties(hello_static PROPERTIES
OUTPUT_NAME "hello" # 去掉"_static"后缀
CLEAN_DIRECT_OUTPUT 1)
```
#### 方法2:对象库避免重复编译
```cmake
# 创建对象库(不直接生成二进制)
add_library(hello_obj OBJECT src1.cpp src2.cpp)
# 从对象库生成动态库
add_library(hello_shared SHARED $<TARGET_OBJECTS:hello_obj>)
# 从对象库生成静态库
add_library(hello_static STATIC $<TARGET_OBJECTS:hello_obj>)
# 统一输出名称为 libhello
set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello")
```
#### 关键说明:
1. **命名冲突解决**
通过添加`_shared`/`_static`后缀区分目标名,再用`OUTPUT_NAME`统一输出文件名[^2]。
2. **避免重复编译**
方法2使用`OBJECT`库技术,源文件仅编译一次,同时用于动/静态库[^3]。
3. **平台兼容性**
Linux下自动生成`.so`/`.a`,Windows下生成`.dll`+`.lib`/`.lib`。
4. **安装支持**
添加安装指令:
```cmake
install(TARGETS hello_shared hello_static
ARCHIVE DESTINATION lib # 静态库
LIBRARY DESTINATION lib) # 动态库
```
> 示例输出文件(Linux):
> - `libhello.so` (动态库)
> - `libhello.a` (静态库)
阅读全文
相关推荐




















