cmake:静态库与动态库

静态库

文件树

├── CMakeLists.txt
├── include
│   └── static
│       └── Hello.h
└── src
    ├── Hello.cpp
    └── main.cpp

CMakeLists.txt

cmake_minimum_required(VERSION 3.8)

project(hello_lib)

############################################################
# Create a library
############################################################

#库的源文件Hello.cpp生成静态库hello_lib
add_library(hello_lib STATIC src/Hello.cpp)

target_include_directories(hello_lib PUBLIC ${PROJECT_SOURCE_DIR}/include)

# target_include_directories为一个目标(可能是一个库library也可能是可执行文件)添加头文件路径

############################################################
# Create an executable
############################################################

# 指定用哪个源文件生成可执行文件
add_executable(hello_binary src/main.cpp)

#连接可执行文件和静态库
target_link_libraries(hello_binary PRIVATE hello_lib)

创建静态库

add_library():用于从某些源文件创建一个库,可以是动态库也可以是静态库,默认生成在构建文件夹

note:如前面的示例所述,将源文件直接传递给add_library调用,这是modern CMake的建议。(而不是先把Hello.cpp赋给一个变量)

cmake中打印某个消息

添加头文件所在的目录

target_include_directories():添加一个目录,包含头文件

使用这个函数后,这个目录会在以下情况被调用:

  • 编译这个库的时候

    因为这个库hello_library由Hello.cpp生成,Hello.cpp中函数的定义在Hello.h中,Hello.h在这个include目录下,所以显然编译这个库的时候,这个目录会用到

  • 编译链接到这个库hello_library的任何其他目标(库或者可执行文件)

private pubic interface的解释

如果源文件(例如CPP)中包含第三方头文件,但是头文件(例如hpp)中不包含该第三方文件头,采用PRIVATE。 如果源文件和头文件中都包含该第三方文件头,采用PUBLIC。 如果头文件中包含该第三方文件头,但是源文件(例如CPP)中不包含,采用 INTERFACE。

  • PRIVATE - 目录被添加到目标(库)的包含路径中。
  • INTERFACE - 目录没有被添加到目标(库)的包含路径中,而是链接了这个库的其他目标(库或者可执行程序)包含路径中
  • PUBLIC - 目录既被添加到目标(库)的包含路径中,同时添加到了链接了这个库的其他目标(库或者可执行程序)的包含路径中

建议:

在include下创建子目录存放各种类型的头文件,在使用target_include_directories()包含头文件时,路径仅仅到include即可,到时候需要引入该头文件时则需要带上子目录,如“static/Hello.h",而不是”Hello.h",虽然更麻烦一些,但是避免了头文件名相同而产生的冲突

链接库

target_link_library():连接库文件,某个可执行文件需要用到这个库时,需要用到此函数告知编译器,并使用add_executable()连接源文件

add_executable(hello_binary
    src/main.cpp
)

target_link_libraries( hello_binary
    PRIVATE
        hello_lib
)

告诉cmake在链接期间将hellolib链接到hello_binary可执行文件。如果这个被链接的库有INTERFACE或者PUBLIC属性的包含目录,那么这个包含目录也会被传递(propagate)给这个可执行文件

public是说,你的这个工程如果被link了,那你的target_link_libraries指定的lib也会被链 private是说,你link的这些libs不会被暴露出去。

比如你的工程B是个dll,public连接了C, D 这个时候你的A.exe要链接B,那么它也会链接C和D 如果B是private链接了C, D 那么A链B的时候,不会链C和D

那么,A.exe链接B的时候,其实也有public和private的选项,但是因为没有其他东西链接A,所以不起作用。 这个主要是针对其它工程链自己的设置

对于hello_binary,它不是库,所以不会被链接。直接private自己用这个库就行。

动态库的构建

在Linux平台下构建动态库的方法和静态库生成的方法类似,只有在使用add_library命令时,参数STATIC改为SHARE即可。
在Windows平台下,在导出动态库时除了会生成.dll动态库之外,还会生成一个.lib文件。注意,这个.lib文件和静态库的.lib文件时不同的,它里面并不保存代码生成的二进制文件,而是所有需要导出符号的符号表。因此这个.lib文件和编译生成的的静态库.lib相比较而言会小的多。在Windows平台下导出dll动态库时若无__declspec(dllexport),依然可以成功的编译出动态库,但是并不会生成保存符号表的.lib文件。

除了导出符号标识符 __declspec(dllexport) 以外,我们作为用户使用动态库的时候,对应头文件中的符号还需要有 __declspec(dllimport) 标识符来表示这个符号是从动态库导入的。对应上面的 MyClass 这个例子,我们包含的头文件应该有以下内容:

所以当需要创建动态库的时候,会进行判断时导入还是导出,将使用不同的关键字

#ifndef __HELLO_H__
#define __HELLO_H__

#ifdef MY_LIB_EXPORTS
#define MY_LIB_API __declspec(dllexport)
#else
#define MY_LIB_API __declspec(dllimport)
#endif


class MY_LIB_API Hello
{
public:
    void print();
};

#endif

#ifdef MY_LIB_EXPORTS表示如果导出,则执行语句#define MY_LIB_API __declspec(dllexport),该语句将__declspec(dllexport)进行宏替换为MY_LIB_API

如果是导出,则将__declspec(dllimport)进行宏替换为MY_LIB_API

还是以之前的工程作为例子,CMakeLists.txt编辑如下:

cmake_minimum_required(VERSION 3.8)

project(hello_dll)

############################################################
# Create a library
############################################################

#库的源文件Hello.cpp生成动态库hello_dll
add_library(hello_dll SHARED src/Hello.cpp)

#给动态库hello_dll起一个别的名字hello::library
add_library(hello::library ALIAS hello_dll)

# 为这个库目标添加头文件路径,PUBLIC表示包含了这个库的目标也会包含这个歌路径
target_include_directories(hello_dll PUBLIC ${PROJECT_SOURCE_DIR}/include)

# target_include_directories为一个目标(可能是一个库library也可能是可执行文件)添加头文件路径

############################################################
# Create an executable
############################################################

# 指定用哪个源文件生成可执行文件
add_executable(hello_binary src/main.cpp)

#连接可执行文件和动态库  使用这个库的别名
target_link_libraries(hello_binary PRIVATE hello::library)

在cmake中打印某个消息:

SET(USER_KEY "Hello World")
MESSAGE( STATUS "this var key = ${USER_KEY}.")

 遇到问题:

在编译Dll文件的时候遇到一个问题,当我需要生成.dll和.lib文件时出现了很多警告提示,说“***”: dll 链接不一致

解决方案:

由于工程文件Hello/h中有使用宏MY_LIB_EXPORTS,所以需要添加宏

解决办法:项目属性-> C/C++ -> 预处理器 ->预处理器定义,添加MY_LIB_EXPORTS即可。

或者在CMakeLists.txt添加命令add_difinitions(-DMY_LIB_EXPORTS)  #注意加上前缀-D

使用add_compile_definitions()则不需要使用前缀

如果使用命令行方式编译,则在编译时使用命令cmake /DMY_LIB_EXPORTS ..

(有参命令使用前缀-D,例如cmake -DMY_LIB_EXPORTS = 100)

同时生成静态库与动态库

aux_source_directory (. SRC_LIST)

# 使用${SRC_LIST}源文件生成一个叫做hello_shared 的动态库
add_library (testFunc_shared SHARED ${SRC_LIST})
# 使用${SRC_LIST}源文件生成一个叫做hello_static 的静态库
add_library (hello_static STATIC ${SRC_LIST})

# 将hello_shared 重新命名
set_target_properties (testFunc_shared PROPERTIES OUTPUT_NAME "testFunc")
# 将hello_static  重新命名
set_target_properties (testFunc_static PROPERTIES OUTPUT_NAME "testFunc")

# 设置 库文件的默认输出路径
set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

在Linux系统中可以通过set_target_properties重命名为一样的名称,但是windows不行,因为windows动态链接库生成静态链接库.dll以及符号目录.lib与静态链接库后缀冲突

cmake命令说明:

  • set_target_properties: 设置输出的名称,还有其它功能,如设置库的版本号等等

预定义变量:

  • LIBRARY_OUTPUT_PATH: 库文件的默认输出路径,这里设置为工程目录下的lib目录
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_54881777

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值