windows--cmake与c++的使用教程(15)
windows--cmake与c++的使用教程(15)
1 概述
- 本文基于前文环境
本节目标:target_link_libraries 为项目链接动态库
2 target_link_libraries 作用
- 常用于链接给定目标或者使用的库。(说人话:链接第三方库, 静态库与动态库)
比如,项目A需要依赖动态库B,那么, 动态库B通常需要提供:头文件、lib库文件和可执行程序dll文件。头文件用于告知动态库中的接口,lib(符号表)文件用于通过编译项目,dll文件用于程序运行时。
3 准备
- 库文件的config.cmake文件: 用于指定动态库的头文件、lib库文件和dll文件
5 顺序
- 先 使用 find_package 找到对应的库, 再调用 target_link_libraries 将库连接到目标中。
- find_package 怎么知道去哪里寻找对应的 XX-config.cmake文件呢? 答案就是CMAKE_PREFIX_PATH 变量,将 XX-config.cmake文件所在路径设置到CMAKE_PREFIX_PATH变量中,即可。 且须放在 find_package 调用之前。 也就是说, 先设置CMAKE_PREFIX_PATH,再调用find_package, 最后是调用target_link_libraries。
下面举例,帮助理解
6 项目概述
- 基于 小程序 源码, 可下载源码,对照参看
- 小程序中,项目 EPA 需要 依赖动态库 OctExeDllVersion
7 OctExeDllVersion库-config.cmake文件
- 作用,用于指定库的头文件所在位置、lib所在位置和dll文件所在位置。
- 文件名: oct_edv-config.cmake
- oct_edv-config.cmake文件内容:
# target_name: 项目名称, config_name:debug 或者 release , bin_root:dll所在路径, bin_name:动态库名, lib_root:lib文件路径, lib_name:lib库文件名
macro(set_lib_edv_properties target_name config_name bin_root bin_name lib_root lib_name)
set_property(TARGET ${target_name} APPEND PROPERTY IMPORTED_CONFIGURATIONS ${config_name})
# 指定动态库: 动态库文件
set(bin_file ${bin_root}/${config_name}/${bin_name})
# 指定 IMPORTED_LOCATION_<CONFIG> 属性
set_target_properties(${target_name}
PROPERTIES
"IMPORTED_LOCATION_${config_name}" ${bin_file}
)
# 指定lib库文件
set(lib_file ${lib_root}/${config_name}/${lib_name})
# 指定 IMPORTED_IMPLIB_<CONFIG> 属性
set_target_properties(${target_name}
PROPERTIES
"IMPORTED_IMPLIB_${config_name}" ${lib_file}
)
endmacro()
# 指定根目录, CMAKE_CURRENT_LIST_DIR: 表示当前config.cmake文件所在路径
set(lib_edv_root
${CMAKE_CURRENT_LIST_DIR}/..
)
if (NOT TARGET oct::edv)
# 建库
add_library(oct::edv SHARED IMPORTED)
endif()
if (TARGET oct::edv)
# 指定头文件属性
set_property(TARGET oct::edv PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${lib_edv_root}/include)
# 指定lib库和dll文件属性
set_lib_edv_properties( oct::edv DEBUG ${lib_edv_root}/bin "OctExeDllVersionD.dll" ${lib_edv_root}/lib "OctExeDllVersionD.lib" )
set_lib_edv_properties( oct::edv RELEASE ${lib_edv_root}/bin "OctExeDllVersion.dll" ${lib_edv_root}/lib "OctExeDllVersion.lib" )
endif()
- config.cmake文件看似很多代码, 其实很简单:
- 1 建库 oct::edv
# 建库
add_library(oct::edv SHARED IMPORTED)
- 2 指定oct::edv头文件路径
# 指定头文件属性
set_property(TARGET oct::edv PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${lib_edv_root}/include)
- 3 指定 lib库文件和dll文件属性
# 指定lib库和dll文件属性
set_lib_edv_properties( oct::edv DEBUG ${lib_edv_root}/bin "OctExeDllVersionD.dll" ${lib_edv_root}/lib "OctExeDllVersionD.lib" )
set_lib_edv_properties( oct::edv RELEASE ${lib_edv_root}/bin "OctExeDllVersion.dll" ${lib_edv_root}/lib "OctExeDllVersion.lib" )
这里用到了 CMAKE 宏 macro。 cmake中与宏对应的还有函数 function。下一文详解
8 设置 oct_edv-config.cmake路径
set(CMAKE_PREFIX_PATH
${CMAKE_PREFIX_PATH}
"C:/major/development/tools/qt/5.14/install/5.14.2/msvc2015_64"
${common_cmake_root}
)
- common_cmake_root 就是用于存放 edv-config.cmake文件的路径, 我这里是: path/CMAKE-EPA/publish/vcXX/x64/cmake
"C:/major/development/tools/qt/5.14/install/5.14.2/msvc2015_64" 是为了链接Qt的库文件
9 调用 find_package
- 调用
# oct_edv
find_package(oct_edv REQUIRED)
- find_package: 有两个参数: 参数1:oct_edv,其中,oct_edv来自oct_edv-config中'-' 前面的oct_edv,参数2: 指定该文件必须找到,否则,Find_package所在CMakeLists.txt脚本解析报错,不会继续执行find_package后面的CMake脚本内容
10 target_link_libraries 链接
- 用法
# 需要链接 自己 专门修改exe和dll的动态库
target_link_libraries(${PROJECT_NAME}
PRIVATE oct::edv
)
- 这里的 oct::edv 是来自 oct_edv-config.cmake 文件中 add_library所创建的项目 oct::edv
已按照上面的步骤顺序,完成库的链接。
附目录结构
.
├─Common
├─EPA
│
├─OctExeDllVersion
|
└─publish
└─vcXX
└─x64
├─bin
│ ├─Debug
│ └─Release
├─cmake
├─include
└─lib
├─Debug
└─Release
Common - 存放通用的cmake脚本文件
EPA - 主程序
OctExeDllVersion - 动态库项目
publish - 动态库和可执行程序的输出目录
11 完整 cmake脚本内容
cmake_minimum_required(VERSION 3.23)
project(EPA)
set(CMAKE_PREFIX_PATH
${CMAKE_PREFIX_PATH}
"C:/major/development/tools/qt/5.14/install/5.14.2/msvc2015_64"
${common_cmake_root}
)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(${linguist_cmake})
# SET_PROPERTY(GLOBAL PROPERTY USE_FOLDERS ON)
set(QT_VERSION 5)
# set(REQUIRED_LIBS Core Gui Widgets)
set(REQUIRED_LIBS_QUALIFIED Core Gui Widgets Sql)
# CMAKE包查找
find_package(Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS_QUALIFIED} REQUIRED)
find_package(oct_edv REQUIRED)
# 指定源码目录
set(proj_root
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# 有这些文件需要纳入编译: 不推荐这样用, 应该一个一个文件的手动加入到这里
file(GLOB_RECURSE
epa_files
${proj_root}/*.h
${proj_root}/*.cpp
${proj_root}/*.inl
)
# 设置build输出目录 PROJECT_BINARY_DIR
set(project_bin_root
${CMAKE_CURRENT_SOURCE_DIR}/resource
)
# 目标目录下的qrc
set(project_qrc
${project_bin_root}/epa.qrc
)
# 拷贝资源到输出目录
# 创建可执行程序项目
add_executable(${PROJECT_NAME}
${epa_files}
${project_qrc}
)
# 翻译文件
set(proj_ts_file
${CMAKE_CURRENT_SOURCE_DIR}/lang/zh_CN.ts
)
# 添加翻译
# 参数1 项目名称, 参数2:输出目录下的qrc, 参数3: ts文件, 参数4:, 参数5:要翻译哪些文件夹
create_qm_file(${PROJECT_NAME} ${project_qrc} ${proj_ts_file} ${project_bin_root}/lang ${proj_root})
# 指定头文件搜索路径
target_include_directories(${PROJECT_NAME}
PRIVATE ${proj_root}
)
# 链接Qt库
target_link_libraries(${PROJECT_NAME}
PRIVATE Qt${QT_VERSION}::Sql Qt${QT_VERSION}::Widgets Qt${QT_VERSION}::Core Qt${QT_VERSION}::Gui
)
# 需要链接 自己 专门修改exe和dll的动态库
target_link_libraries(${PROJECT_NAME}
PRIVATE oct::edv
)
# 拷贝配置文件到输出路径
copy_folder_macro(${CMAKE_CURRENT_SOURCE_DIR}/fr ${pbd})
copy_folder_macro(${CMAKE_CURRENT_SOURCE_DIR}/fr ${pbr})
# query
copy_folder_macro(${CMAKE_CURRENT_SOURCE_DIR}/vswhere ${pbd})
copy_folder_macro(${CMAKE_CURRENT_SOURCE_DIR}/vswhere ${pbr})
# 如果是windows,设置管理员权限。注意使用if需要配合endif使用
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "/level='requireAdministrator'" )
endif()
# 关闭控制台
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
endif()