cmake引入动态库
终于跟着恩培的教程摸了一边cmake导包的过程, 希望以后不会再那么多疑惑跟问题.
首先, cmake不同版本的差异很挺大, 我环境是3.16.3, 应该还挺算新, 3.10有问题.
首先, 弄一个动态的库, 用于给别人导入, 类似ffmpeg, opencv这类第三方的库.
build文件夹是cmake自己生成的, 不用管.
src里面就是源码, 由于是个库, 不需要main函数
include里面是头文件目录.
关键是CMakLists.txt:
cmake_minimum_required(VERSION 3.10)
project(custom_mod)
set(CMAKE_CXX_STANDARD 11)
# 将dlib编译成动态库
add_library(dlib SHARED src/dlib.cpp)
# 设置头文件目录
target_include_directories(dlib PUBLIC include)
# 设置动态库的头文件输出目录
set_target_properties(dlib PROPERTIES PUBLIC_HEADER include/dlib.h)
# 安装动态库, 仅安装动态库, 头文件, 没有可执行文件,静态库
install(TARGETS dlib
# RUNTIME DESTINATION bin
# ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
PUBLIC_HEADER DESTINATION include
)
通过add_library, 生成dlib库, 后面加个SHARED表示动态库
构建的时候:使用下面的命令, 提前加入install的目录, 即修改默认的install目录
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/home/marc/cmake_demo/find_demo/custom_mod/install
cmake --build build
cmake -install build
这样就在这个目录中构建, 编译, 安装好了, 类似开头那个图中的结构
动态库的部分进本就是这样, 如果你想使用这个动态库, 咋用呢?
这里再提一句, 如果你是用apt安装的包, 例如ffmpeg, opencv等, 或者你下载的源码, 用cmake/make安装过, 直接find_package即可, cmake会根据pc, 即package-config内容去搜索, 不需要在cmake目录下建cmake模块文件了.
如果你是下载的比如ffmpeg/opencv或者其他库的源码, 仅仅执行了编译, 而没有make install或者cmake --install, 或者是咱们这种情况, 直接用的编译安装的库, 就按照下面来:
分以下几步:
首先, 正常的建本项目的src, include, 根目录建一个CMakeLists.txt, 插入一段:
# 设置引入的cmake模块文件的路径
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
这一步尤为重要, 这一步是告诉本项目构建的时候, 需要去找一下本项目的cmake目录下的cmake模块, 基本算是一个include吧, 这个模块本质上就是一个对动态库的搜索的代码片段.
list的意思就是一个项目可能存在多个动态库, 那么这个cmake文件夹就有多个cmake文件.
然后加一行:
find_package(dynamiclib REQUIRED)
然后在本项目下面建cmake文件夹, 并在cmake文件夹下面, 建一个Findxxx.cmak的文件, xxx即库名, 这里就是上面的dynamiclib, 即这个文件名是Finddynamiclib.cmake
# 在dlib_INCLUDE_DIR查找dlib.h
find_path(dlib_INCLUDE_DIR dynamiclib.h PATHS ${DLIB_INSTALL_PATH}/include)
# 在dlib_LIBRARY查找libdynamic_lib.lib
find_library(dlib_LIBRARY dynamiclib PATHS ${DLIB_INSTALL_PATH}/lib)
if(dlib_INCLUDE_DIR AND dlib_LIBRARY)
message("found dlib")
set(dlib_FOUND TRUE)
# 设置一个自定义的变量
set(dlib_VERSION 1.0.0)
# 获取dlib库的目录
get_filename_component(dlib_LIBRARY_DIR ${dlib_LIBRARY} DIRECTORY)
endif()
find_path其实就是搜索目录, 一旦找到就dynamiclib.h, 就给dlib_INCLUDE_DIR赋值, 搜索的地址就是${DLIB_INSTALL_PATH}/include
而这个DLIB_INSTALL_PATH变量, 可以在主项目目录的CMakeLists.txt中定义, 也可以当作参数-D进行输入, 代表这个动态库的具体install文件夹.
find_library, 就是查找库文件, 搜到的其实就是库文件
下面的if找到库文件跟头文件路径, 就设置一个变量dlib_FOUND为true, 同时设置一个dlib版本, 自定义的, 后面在主CMakeLists.txt中, 可以访问这个变量, 以检测是否能找到库文件.
最后get_filename_component用于进一步设置一下库文件的路径, 刚才找到的是库文件本身, 通过这个可以获取库文件的目录.
最后回到主项目的CMakeLists.txt
# 设置一个缓存变量, 给Findxxxlib.cmake使用
set(DLIB_INSTALL_PATH "./lib" CACHE PATH "dlib的安装路径")
message(STATUS "dlib的安装路径为: ${DLIB_INSTALL_PATH}")
这一句的作用是给动态库设置一个默认的位置, 这一句其实我发现可有可无, 因为在构建阶段会传入这个参数, 把动态库的安装路径通过DLIB_INSTALL_PATH传进来.
if(dlib_FOUND)
message(STATUS "dlib found")
message(STATUS "dlib的头文件路径为: ${dlib_INCLUDE_DIR}")
message(STATUS "dlib的库文件路径为: ${dlib_LIBRARY}")
message(STATUS "dlib_VERSION: ${dlib_VERSION}")
else()
message(FATAL_ERROR "dlib not found")
endif()
这个dlib_FOUND就是刚才模块cmake里面, 当找到了动态库的时候会赋值为TRUE, 这里对上面的find_package动作进行检查, 如果构建的时候可以看到动态库的文件路径, 表示能找到对应的头文件目录跟库文件, 以及自己设置的那个自定义的版本号.
# 用于install之后, 生成的可执行文件能找到动态库
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH ${dlib_LIBRARY_DIR})
这两句目的是让这个可执行程序在安装后, 依然能找到动态库, 这个能解决一些例如你做跨平台的编译, 交叉编译, 或者你的程序在别人的机器上跑不起来提示找不到动态库, 可以考虑是否是这个原因.
add_executable(main_app src/main.cpp)
主程序的需要编译成可执行文件
# 给要引用的头文件添加路径
target_include_directories(main_app PUBLIC ${dlib_INCLUDE_DIR})
# 给要引用的库添加路径
target_link_libraries(main_app ${dlib_LIBRARY})
分别为可执行文件target添加动态库以及头文件路径.
install(TARGETS main_app
RUNTIME DESTINATION bin
)
设置主程序的安装目录.
最后的构建, 编译, 安装命令为:
cmake -S . -B build -DDLIB_INSTALL_PATH=/home/marc/tensorrt_demo/find_101/dynamiclib/install -DCMAKE_INSTALL_PREFIX=/home/marc/cmake_demo/find_demo/custom_mod/install
cmake --build build
cmake --install build
遗留的问题, 接下来试试ffmpeg的引入.