使用 CMake 构建 Qt 动态库模块
1. 项目结构与需求分析
典型 Qt 动态库项目布局
ProjectRoot/
├── CMakeLists.txt # 主配置
├── app/
│ ├── CMakeLists.txt # 主应用程序配置
│ └── main.cpp
├── libs/
│ ├── CoreLibrary/ # 动态库模块1
│ │ ├── CMakeLists.txt
│ │ ├── CoreLibrary.h
│ │ └── CoreLibrary.cpp
│ └── NetworkModule/ # 动态库模块2
│ ├── CMakeLists.txt
│ ├── NetworkModule.h
│ └── NetworkModule.cpp
└── resources/ # Qt 资源文件(可选)
核心需求
- 模块化管理:将功能拆分为独立的动态库
- 跨平台兼容:兼容 Windows(DLL)、Linux(.so)和 macOS(.dylib)
- 正确的路径管理:确保构建和运行时依赖路径正确
- Qt 特性支持:自动处理 MOC、UIC 和 RCC 的生成
2. 基础 CMake 配置(Qt 集成)
CMake 主文件骨架
cmake_minimum_required(VERSION 3.20)
project(MyQtApp VERSION 1.0 LANGUAGES CXX)
# 全局默认设定:可执行文件和 DLL 到 bin/, 静态库和导入库(.dll.a)到 lib/
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 查找 Qt 依赖
find_package(Qt6 COMPONENTS Core Gui Widgets Network REQUIRED)
# 启用自动处理 MOC/UIC/RCC
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTUIC ON)
set(CMAKE_AUTORCC ON)
file(GLOB SOURCE_CPP ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
file(GLOB SOURCE_H ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
set(SOURCE_FILE
${SOURCE_CPP}
${SOURCE_H}
)
# 添加子目录
add_subdirectory(libs/CoreLibrary)
add_subdirectory(libs/NetworkModule)
add_subdirectory(app)
add_executable(${PROJECT_NAME} ${SOURCE_FILE})
target_link_libraries(${PROJECT_NAME} Qt6::Widgets CoreLibrary NetworkModule)
3. 动态库模块的创建与配置
示例:子模块CoreLibrary
动态库的 CMakeLists及CoreLibrary.h
# libs/CoreLibrary/CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(CoreLibrary VERSION 1.0 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 REQUIRED COMPONENTS Widgets)
set(SOURCE_FILE
CoreLibrary.cpp
CoreLibrary.h
CoreLibrary.ui
)
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILE})
target_compile_definitions(${PROJECT_NAME} PRIVATE CORELIBRARY_LIBRARY)
# 显式设置运行时 .dll 到 bin/, 导入库到 lib/
# 跨平台输出路径配置 (根据平台优化)
if(WIN32)
# Windows: DLL → bin/, 导入库 → lib/
set_target_properties(${TARGET_NAME} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
)
else()
# Linux/macOS: 共享库 → lib/
set_target_properties(${TARGET_NAME} PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
)
endif()
# 指定库的包含目录
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(${PROJECT_NAME} Qt6::Widgets)
# libs/CoreLibrary/CoreLibrary.h
#ifndef CORELIBRARY_H
#define CORELIBRARY_H
#include <QWidget>
#if defined(CORELIBRARY_LIBRARY)
#define CORELIBRARY_EXPORT Q_DECL_EXPORT
#else
#define CORELIBRARY_EXPORT Q_DECL_IMPORT
#endif
QT_BEGIN_NAMESPACE
namespace Ui {
class CoreLibrary;
}
QT_END_NAMESPACE
class CORELIBRARY_EXPORT CoreLibrary: public QWidget {
Q_OBJECT
public:
explicit CoreLibrary(QWidget *parent = nullptr);
...
4.总结
1.指定生成动态库
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILE})
2.在编译时定义一个预处理宏 CORELIBRARY_LIBRARY(自定义)
target_compile_definitions(${PROJECT_NAME} PRIVATE CORELIBRARY_LIBRARY)
3.设置 TARGET_NAME
目标的特定属性。主要控制 可执行文件和库的输出目录
# 显式设置运行时 .dll 到 bin/, 导入库到 lib/
# 跨平台输出路径配置 (根据平台优化)
if(WIN32)
# Windows: DLL → bin/, 导入库 → lib/
set_target_properties(${TARGET_NAME} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
)
else()
# Linux/macOS: 共享库 → lib/
set_target_properties(${TARGET_NAME} PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
)
endif()
4. 控制符号导出/导入
Q_DECL_EXPORT
(导出):
当NDBPOOL_LIBRARY
宏被定义时,说明当前代码正在编译 动态库,需要将库中的符号(类、函数)导出,以便其他应用使用。Q_DECL_IMPORT
(导入):
当NDBPOOL_LIBRARY
未定义 时,说明当前代码是一个使用该库的应用程序,需要导入符号。
如:
#if defined(CORELIBRARY_LIBRARY)
#define CORELIBRARY_EXPORT Q_DECL_EXPORT
#else
#define CORELIBRARY_EXPORT Q_DECL_IMPORT
#endif
5.标记 需要导出或导入 的类
class NDBPOOLSHARED_EXPORT MyClass
6.在主CMakeLists文件中设置默认输出目录
# 全局默认设定:可执行文件和 DLL 到 bin/, 静态库和导入库(.dll.a)到 lib/
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)