阅读3分钟

本节教程对应的 GitHub示例

之前我们学到了如何编写一个可执行程序和Library,在继续学习之前,需要解释下 target ,在cmake中我们可以给 executable library 设置一个target名字,这样可以方便我们在后续对target进行更加详细的属性设置。

本节我们将学习如何在项目中引用lib,相关的api有:

link_libraries(<item>... ...) # item链接到所有目标中
target_link_libraries(<target> ... <item>... ...) # 将item链接到指定的target中

其中item参数支持的情况比较多,提前介绍几种情况:

  • lib name:提供库的名字,让cmake去查找对应的库文件绝对路径
  • lib fullpath:直接一步到位给出库文件的绝对路径
  • target name: 根据target的属性,让cmake查找对应库文件的绝对路径
  • target_xxx的命令颗粒度更细,是比较高版本的命令。

    app调用有源代码的lib

    这种情况是有lib的源代码,我们将lib以源代码的方式链接到项目内

    项目结构为:

  • main.cpp
  • lib.cpp
  • CMakeLists.txt
  • # app
    cmake_minimum_required(VERSION 3.0.0) 
    set(app "demo") 
    project(${app} 
    VERSION 0.1.0) 
    add_executable(${app} main.cpp)
    # lib
    set(lib "my-lib") 
    project(${lib}) 
    add_library(${lib} ./lib.cpp)
    # 将lib链接到app
    target_link_libraries(${app} ${lib}) 
    

    需要注意的target_link_libraries

  • 第一个target参数来自add_executable的第一个参数
  • 后续item参数来自add_library的第一个参数
  • app调用第三方lib、dll

    这种情况是我们使用第三方的lib,只有头文件和库文件(.a、.dll、*.so

    方式1:imported-libraries

    # 设置target名字为dll,并且是一个外部导入的lib
    # GLOBAL选项可以将target的作用域变为全局,默认是只在目录内可见
    add_library(dll SHARED IMPORTED GLOBAL)
    # 设置target dll的详细属性,dll/lib 不区分debug/release的情况
    set_target_properties(dll PROPERTIES
        # 指向lib,windows必须设置此项
        IMPORTED_IMPLIB ${CMAKE_CURRENT_LIST_DIR}/dll.lib
        # 指向dll、so等
        IMPORTED_LOCATION ${CMAKE_CURRENT_LIST_DIR}/dll.dll 
    # 设置debug/release
    set_target_properties(dll PROPERTIES
        IMPORTED_IMPLIB_DEBUG ${CMAKE_CURRENT_LIST_DIR}/dll_debug.lib
        IMPORTED_IMPLIB_RELEASE ${CMAKE_CURRENT_LIST_DIR}/dll_release.lib
        IMPORTED_LOCATION_DEBUG ${CMAKE_CURRENT_LIST_DIR}/dll_debug.dll
        IMPORTED_LOCATION_RELEASE ${CMAKE_CURRENT_LIST_DIR}/dll_release.dll
    target_link_libraries(app dll)
    

    这里我们使用的是set_target_properties,可以同时给多个target设置多个不同的属性:

    set_target_properties(
        target1 target2 ...  
        PROPERTIES 
            prop1 value1  
            prop2 value2 
    

    方式2:将指定的库文件直接链接到target的不同配置

    target_link_libraries(${target} debug ${debug_fullpath})
    target_link_libraries(${target} optimized ${release_fullpath})
    

    这两种方式都可以,原因是target_link_librariesitem参数既可以是target name,也可以是lib fullpath

    在xcode中,对应的工程配置为:

    当为target name时,出现在command的args中

    当为lib fullpath时,在xcode中的位置在Linking/Others Linker Flags:

    无论使用哪种方式,注意dll、lib必须是存在的,底层查找lib时,不会自动补充lib前缀。

    include头文件目录问题

    以上仅仅是告诉编译器lib文件的位置在哪里,我们需要添加头文件目录,编译器才能识别到lib api

    方式1:直接添加头文件目录,缺点是要手动把使用到的lib include path一个一个加进去,比较麻烦

    # 注意:要放在add_executable、add_library前边才有效
    include_directories("a/b/c/")
    

    方式2:给lib target中添加,只要项目依赖了这个target,就会自动将目录添加到项目中

    # 注意:要放在add_library后边才有效
    target_include_directories(lib
        INTERFACE      # 权限控制,类比:public/private/protected
            ${CMAKE_CURRENT_SOURCE_DIR}/
    

    方式3:和方式2没啥区别,写法不同而已

    set_property(TARGET lib
        PROPERTY 
            INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}
    

    推荐使用第2种方式。

    lib包含目录问题

    这种情况对应的是我们target_link_libraries的item参数是lib name,为了让CMake可以通过lib name找到对应的lib文件 ,所以我们才需要设置库包含目录,如果item参数是lib pathtarget name,则不需要设置库包含目录。

    相关的命令有link_directoriestarget_link_directories,区别参考上文。