CMake - 如何链接到我的项目打包的外部库?

Posted

技术标签:

【中文标题】CMake - 如何链接到我的项目打包的外部库?【英文标题】:CMake - How Can I link to an external library that I have packed with my project? 【发布时间】:2016-04-23 15:45:06 【问题描述】:

如何链接到我的项目打包的外部库?

这是我的项目的设置方式:

我有一个名为 App 的文件夹,这是我的 main.cpp 所在的位置。

C:\Raph\src\App

main.cpp

我还有一个名为“ExternalLibrary”的文件夹,我在其中捆绑了需要在项目中使用的 Qt 库:

C:\Raph\src\ExternalLibrary\Platform\Windows\Qt5.6\VS2013_64bit

它包含 3 个文件夹:

**bin**
    moc.exe
    rcc.exe
    uic.exe
    ......
    bunch of Qt dll files

**include**
    bunch of Qt header files

**lib**
    bunch of Qt lib files 

我需要设置 Cmake 来做三件事:

    链接到我动态打包到“ExternalLibrary”中的 Qt 库。

    每次在项目中添加 Qt 类或资源时自动执行 moc.exe、uic.exe、rcc.exe。我也不需要任何 生成的 moc_myClassname.cpp 将显示在项目中。

    我希望所有 Qt dll 最终都放在可执行文件所在的 bin 文件夹中。例如:

    C:\Raph\build\Raph\Windows\x64\Debug\bin

    Raph.exe + 所有必要的 Qt Dlls

【问题讨论】:

太宽泛:几个不相关的问题。简而言之:1.谷歌find_package + QT。 2. 自动显示新的源文件不是 CMake 的特性。通常每次添加/删除源时都需要修改CMakeLists.txt。 3. 只需创建将文件复制到目标目录的规则。尝试专注于单个问题并在询问之前对其进行基本搜索。 【参考方案1】:

(1) 您可以通过以下方式找到 Qt 库:

find_package(Qt5Core [...] 5.6 REQUIRED)
add_executable(someExe file1.cpp)
target_link_libraries(someExe Qt5::Core)

请注意,您应该保留 Qt 的原始目录结构,还包含 Qt cmake 脚本,例如lib/cmake/Qt5/Qt5Config.cmake

如果您希望在搜索 Qt 时包含特定目录,您可以这样做(在搜索 Qt 之前):

set(CMAKE_PREFIX_PATH $CMAKE_PREFIX_PATH "./your/path")

例如,这可以是您提供的目录。

(2) 您可以使用 cmake 的 automoc 功能。只需添加

set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)

在您的 cmake 脚本的开头。 CMake 还支持AUTOUICAUTORCC。我还没有尝试过,但可能工作类似。

(3) 例如,您可以将自定义目标添加到您的项目中,以复制 dll。构建该目标时,将复制所有 dll。您可以从 Qt 查找脚本定义的目标(如 Qt5::Core)中获取 dll 的路径。

get_target_property(LOC_R Qt5::Core LOCATION_RELEASE)
get_target_property(LOC_D Qt5::Core LOCATION_DEBUG)

但是,您还应该扫描这些目标的依赖项(它们依赖的其他 dll)。您可以编写一个宏来扫描整个目标列表以查找对应的 dll 并将它们添加到列表中,我们将它们称为 RELEASE_DLLSDEBUG_DLLS

macro(copydlls RELEASE_DLLS DEBUG_DLLS MODULELIST)
    foreach(ELEMENT $$MODULELIST)
        get_target_property(LOC_R $ELEMENT LOCATION_RELEASE)
        get_target_property(LOC_D $ELEMENT LOCATION_DEBUG)
        list(APPEND $RELEASE_DLLS $LOC_R)
        list(APPEND $DEBUG_DLLS $LOC_D)


        get_target_property(DEPENDENCIES_RELEASE $ELEMENT IMPORTED_LINK_DEPENDENT_LIBRARIES_RELEASE)
        foreach(DEPENDENCY $DEPENDENCIES_RELEASE)
            if(TARGET $DEPENDENCY)
                get_target_property(LOC_R $DEPENDENCY LOCATION_RELEASE)
                if($LOC_R MATCHES ".dll$")
                    list(APPEND $RELEASE_DLLS $LOC_R)
                endif()
            endif()
        endforeach()    

        get_target_property(DEPENDENCIES_DEBUG $ELEMENT IMPORTED_LINK_DEPENDENT_LIBRARIES_DEBUG)
        foreach(DEPENDENCY $DEPENDENCIES_DEBUG)
            if(TARGET $DEPENDENCY)
                get_target_property(LOC_D $DEPENDENCY LOCATION_DEBUG)
                if($LOC_D MATCHES ".dll$")
                    list(APPEND $DEBUG_DLLS $LOC_D)
                endif()         
            endif()
        endforeach()    

        get_target_property(DEPENDENCIES_RELEASE $ELEMENT IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE)
        foreach(DEPENDENCY $DEPENDENCIES_RELEASE)
            if(TARGET $DEPENDENCY)
                get_target_property(LOC_R $DEPENDENCY LOCATION_RELEASE)
                if($LOC_R MATCHES ".dll$")
                    list(APPEND $RELEASE_DLLS $LOC_R)
                endif()
            endif()
        endforeach()    

        get_target_property(DEPENDENCIES_DEBUG $ELEMENT IMPORTED_LINK_INTERFACE_LIBRARIES_DEBUG)
        foreach(DEPENDENCY $DEPENDENCIES_DEBUG)
            if(TARGET $DEPENDENCY)
                get_target_property(LOC_D $DEPENDENCY LOCATION_DEBUG)
                if($LOC_D MATCHES ".dll$")
                    list(APPEND $DEBUG_DLLS $LOC_D)
                endif()
            endif()
        endforeach()

    endforeach()
endmacro()

然后你可以通过调用这个宏来获得一个列表中的所有 Qt dll:

IF(MSVC)
    set(QT_MODULES "Qt5::Core" "Qt5::Gui" "Qt5::Widgets")
    set(RELEASE_DLLS)
    set(DEBUG_DLLS)
    copydlls(RELEASE_DLLS DEBUG_DLLS QT_MODULES)    
ENDIF(MSVC)

检索到该信息后,您可以通过以下方式创建自定义目标。假设您在RELEASE_DLLSDEBUG_DLLS 列表中拥有所有的dll 路径,在TARGETS 中拥有您的可执行文件名称作为列表。然后你可以这样做:

if(MSVC)

    set(COPY_COMMAND_RELEASE "-E copy_if_different ")
    set(COPY_COMMAND_DEBUG "-E copy_if_different ")

    list(REMOVE_DUPLICATES RELEASE_DLLS)
    list(REMOVE_DUPLICATES DEBUG_DLLS)

    foreach(DLL $RELEASE_DLLS)
        string(CONCAT COPY_COMMAND_RELEASE "$COPY_COMMAND_RELEASE \"$DLL\" ")
    endforeach()
    foreach(DLL $DEBUG_DLLS)
        string(CONCAT COPY_COMMAND_DEBUG "$COPY_COMMAND_DEBUG \"$DLL\" ")
    endforeach()

    string(CONCAT COPY_COMMAND_RELEASE $COPY_COMMAND_RELEASE "\"$CMAKE_CURRENT_BINARY_DIR/Release\" ")
    string(CONCAT COPY_COMMAND_DEBUG $COPY_COMMAND_DEBUG "\"$CMAKE_CURRENT_BINARY_DIR/Debug\" ")

    add_custom_target(COPY_DLLS)

    foreach(EXECUTABLE $TARGETS)
        add_custom_command(TARGET COPY_DLLS PRE_BUILD COMMAND $CMAKE_COMMAND $COPY_COMMAND_RELEASE COMMENT "Copying dlls to executable directory...")   
        add_custom_command(TARGET COPY_DLLS PRE_BUILD COMMAND $CMAKE_COMMAND $COPY_COMMAND_DEBUG COMMENT "Copying dlls to executable directory...")
    endforeach()

endif()

如果您希望每次构建其他目标之一时都执行此目标,您可以这样做:

foreach(EXECUTABLE $TARGETS)
    add_dependencies($EXECUTABLE COPY_DLLS)
endforeach()

不要忘记您还必须将 platforms 目录从 plugins 复制到可执行文件夹,例如将其添加到复制 dll 目标:

add_custom_command(TARGET COPY_DLLS PRE_BUILD 
    COMMAND $CMAKE_COMMAND -E copy_directory $PATH_QT_ROOT/plugins/platforms $CMAKE_CURRENT_BINARY_DIR/Debug/platforms
    COMMAND $CMAKE_COMMAND -E copy_directory $PATH_QT_ROOT/plugins/platforms $CMAKE_CURRENT_BINARY_DIR/Release/platforms
    COMMENT "Copying Qt platforms to executable directory...")

【讨论】:

以上是关于CMake - 如何链接到我的项目打包的外部库?的主要内容,如果未能解决你的问题,请参考以下文章

如何在我的项目中链接 re2 库,就像使用 cmake 的静态库一样

注册。使用 CMake 将外部库链接到项目

CMake 链接到外部库

如何正确地将库与 cmake 链接?

如何在 CMake 项目中包含外部库

在 CMake 中链接外部预建库 [重复]