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的引入.