Windows+MinGW使用CMake生成.dll动态链接库
序言
在完成接口开发后,为了给其他开发人员调用,通常需要将这部分代码打包生成动态链接库文件。
动态链接库很好地保证了代码的封装性和独立性,作为接口,它可以很好地独立于主程序,便于更新。调用接口的主程序编译时也会绕过已生成的动态链接库,仅作连接,在运行时使用它,减少了前端人员编译的时间消耗。
同时由于动态链接库很难被反编译,只有头文件是可见的,这也同时保证了代码的安全性。
在Windows系统上,以.dll为后缀的是动态链接库,以.a结尾的是静态链接库。
静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件;动态链接就是把调用的函数所在文件模块(DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因此需要相应DLL文件的支持。
*Linux系统上的动态链接库后缀为.so,静态链接库后缀还是.a。本篇不做过多介绍。
环境概览
- 操作系统:Windows 10
- 编译器:MinGW-w64
目录结构
│ CMakeLists.txt
│
├─build
│
├─examples
│ test_api.cpp
│
├─include
│ └─detectionapi
│ DetectionApi.h
│
├─lib
│ libopencv_calib3d450.dll
│ libopencv_core450.dll
│
└─src
DetectionApi.cpp
编写CMakeLists.txt
首先,为保证目录结构的干净,先将待编译的文件整理为上述目录结构。 上述目录中,include存放头文件(.h, .hpp),src存放源文件(.c, .cpp),examples存放测试代码,lib存放各种支持程序运行的链接库。CMakeLists.txt放在根目录下,将编译生成的内容全部放在build目录下。
然后开始编辑CMakeLists.txt文件,一个样例如下所示。
cmake_minimum_required(VERSION 3.9.0)
project(detection)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/detectionapi)
include_directories(../opencv/build/include)
include_directories(../opencv/build/include/opencv2)
file(GLOB_RECURSE INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/include/detectionapi/*.h)
LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/lib)
file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
set(LINK_LIBS
libopencv_calib3d450.dll
libopencv_core450.dll
)
ADD_LIBRARY(detectionapi SHARED ${INCLUDES} ${SOURCES})
TARGET_LINK_LIBRARIES(detectionapi ${LINK_LIBS})
INSTALL(TARGETS detectionapi DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/lib)
SET_TARGET_PROPERTIES(detectionapi PROPERTIES LINKER_LANGUAGE C
ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib
LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib
RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib
OUTPUT_NAME "DetectionApi"
PREFIX "")
add_executable(detection examples/test_api.cpp ${INCLUDES})
TARGET_LINK_LIBRARIES(detection ${LINK_LIBS})
TARGET_LINK_LIBRARIES(detection ${PROJECT_BINARY_DIR}/lib/libDetectionApi.dll.a)
如上述样例所示,一个标准的CMake文件包含了以下几大部分:
- 基础配置:完成一些CMake所需的基础配置,包括了版本要求,项目名称,C++标准等。
- 输入配置:所有需要的头文件,源文件,外部链接库。
- 输出配置:生成内容,生成属性等。包括了输出路径,输出类别(可执行程序,dll库等)。
上述示例,基础配置和输入配置是比较显而易见的,这里着重说一下输出配置。
ADD_LIBRARY(detectionapi SHARED ${INCLUDES} ${SOURCES}) 声明这是一个lib库,第一个参数是链接库属性,第二个是类型(SHARED表示动态链接库),第三个参数是输入参数中的头文件和源文件。
TARGET_LINK_LIBRARIES(detectionapi ${LINK_LIBS}) 这个是链接的第三方库,如自行编译出的opencv的各种dll等。
INSTALL(TARGETS detectionapi DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/lib) dll的实际安装指令,按照上述写法将被安装在/lib目录下。
SET_TARGET_PROPERTIES(detectionapi PROPERTIES LINKER_LANGUAGE C
ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib
LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib
RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib
OUTPUT_NAME "DetectionApi"
PREFIX "")
设置dll属性的实际内容。
- PROPERTIES LINKER_LANGUAGE C:声明为C链接库,提高DLL库的兼容性。
- OUTPUT_NAME:DLL库输出名称。
- PREFIX:DLL库前缀,若不设置,可能会加默认前缀。
add_executable(detection examples/test_api.cpp ${INCLUDES}) 生成可执行程序,这里是利用examples/test_api.cpp 来测试封装的dll是否生效。程序主要就是引用了DetectionApi.h,调用给出的函数,观察是否有打印信息。可执行程序仅用作测试dll,没有实际意义,CMake文件可以不包含这段代码。
TARGET_LINK_LIBRARIES(detection ${PROJECT_BINARY_DIR}/lib/libDetectionApi.dll.a) 为了测试dll,就不包含cpp,而是使用静态链接库替代cpp进行编译(原来exe的生成方式是.h + .cpp,现在是.h + .dll.a)。这样,可执行程序在运行时就会去同级目录下找你的dll文件了。而dll.a在此之后也就没有用了。
需要注意的是,本文操作全过程不需要修改原始代码,CMake过程是独立于代码之外的。
CMake语法内容非常庞大,在实际使用中,要多查阅CMake官方文档。 CMake Documentation
编译
可以在build/目录下通过cmake .. 命令,或者通过CMake-GUI编译到build/目录下。 然后在build/下通过make 命令执行生成的Makefile脚本,生成可执行程序和.dll文件。 最后通过make install 在指定位置/lib生成.dll文件。
完成后新的目录结构为:
│ CMakeLists.txt
│
├─build
│ ├─bin
│ │ detection.exe
│ ├─CMakeFiles
│ ├─lib
│ │ detectionapi.dll
│ │ libdetectionapi.dll.a
│ └─Makefile
│
├─examples
│ test_api.cpp
│
├─include
│ └─detectionapi
│ DetectionApi.h
│
├─lib
│ detectionapi.dll
│ libdetectionapi.dll.a
│ libopencv_calib3d450.dll
│ libopencv_core450.dll
│
└─src
DetectionApi.cpp
测试
生成的可执行程序位于build/bin下,将所有需要的第三方dll库放在同级目录下,并将DetectionApi.dll 也放于此。双击.exe,成功运行。 完成dll封装,给其他开发人员调用时,就只需要提供.h和.dll了,非常的方便。
|