静态库
文件树
├── 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目录