在 Qt 中如何使用 CMake 在主项目之前构建依赖库?

How do you build depended-upon libs before the main project with CMake in Qt?

我看过类似的问题,但没有找到针对这种基本情况的问题。我对 CMake 比较陌生。我有一个基于 CMake 的 Qt 5 项目。这是一个简单的测试应用程序;在其构建期间,我想构建并静态 link 开源 Paho MQTT C 库和它的 C++ 包装器。这是两个独立的项目,有自己的 CMakeLists.txt 文件。

根据 Qt 的默认设置,它构建到源代码树之外的目录中。

我已经将这些开源库的源代码树复制到我的项目的父目录下,并将顶级 CMakeLists.txt 文件编辑为 add_subdirectory 它们。

我也加了target_link_libraries。我可以自己获取 C 库来构建和 link 到父项目中,但是如果我添加 C++ 包装器,C++ 包装器的处理会抱怨它找不到 C 库...这是是的,因为它还没有建成。通过简单地使用 Qt 的“构建所有项目”菜单项解决了对 C 库的类似投诉,但是当添加了 C++ 包装器库时,这不起作用。包装器的 CMakeLists.txt 文件发出这个:

CMake Error at paho.mqtt.cpp/src/CMakeLists.txt:150 (message): Could not find Paho MQTT C library

果然它不存在,因为在完成此预处理时它尚未构建。

更新:这是我的顶级 CMakeLists.txt 文件,根据 Corristo 的建议进行了修改,它成功地让 CMake 解析了整个层次结构。该项目现在构建。不过,令我困惑的是这里的最后两行导致了一个空字符串。 link 目录的类似尝试也是如此。

find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)

set(PROJECT_SOURCES
        main.cpp
        mainwindow.cpp
        mainwindow.h
        mainwindow.ui
    task.h
    task.cpp
    task.ui
)

if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
    qt_add_executable(MQTTTest
        ${PROJECT_SOURCES}
    )
else()
    if(ANDROID)
        add_library(MQTTTest SHARED
            ${PROJECT_SOURCES}
        )
    else()
        add_executable(MQTTTest
            ${PROJECT_SOURCES}
        )
    endif()
endif()

add_subdirectory(paho.mqtt.c)
set(PAHO_MQTT_C_LIB paho-mqtt3a)
set(PAHO_MQTT_C_PATH "${CMAKE_CURRENT_LIST_DIR}/paho.mqtt.c")
add_subdirectory(paho.mqtt.cpp)

target_link_libraries(MQTTTest PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
target_link_directories(MQTTTest PUBLIC
                        "${CMAKE_CURRENT_LIST_DIR}/build/paho.mqtt.c/src"
                        "${CMAKE_CURRENT_LIST_DIR}/build/paho.mqtt.cpp/src")

target_link_libraries(MQTTTest PUBLIC paho-mqtt3a)
target_link_libraries(MQTTTest PUBLIC paho-mqttpp3)

target_include_directories(MQTTTest PUBLIC
                        "${PROJECT_BINARY_DIR}"
                        "${CMAKE_CURRENT_LIST_DIR}/paho.mqtt.c/src"
                        "${CMAKE_CURRENT_LIST_DIR}/paho.mqtt.cpp/src")

get_property(inc_dirs DIRECTORY PROPERTY INCLUDE_DIRECTORIES)
message("Top-level include dirs = ${inc_dirs}")

这有点 hack,但您可以利用 CMake find_* 命令在结果变量已设置的情况下不执行搜索这一事实。

paho.mqtt.cpp/src/CMakeLists.txt 文件中我们发现 find_library 的输出变量被称为 PAHO_MQTT_C_LIB 并且 include 目录应该在 PAHO_MQTT_C_INC_DIR 中,这在original CMakeLists.txt from version 1.0.0(这似乎是您正在使用的版本)本身是根据 PAHO_MQTT_C_PATH.

计算得出的

在两个 add_subdirectory 调用之间设置这两个变量应该可以使这个工作:

add_subdirectory(paho.mqtt.c)
set(PAHO_MQTT_C_LIB paho-mqtt3a)
set(PAHO_MQTT_C_PATH "${CMAKE_CURRENT_LIST_DIR}/paho.mqtt.c")
add_subdirectory(paho.mqtt.cpp)

这利用了 target_link_libraries 可以用库文件(这是原始 paho.mqtt.cpp 项目所期望的)和现有的 CMake 目标(这是我们替换它的东西)调用的事实和)。链接到 CMake 目标也会自动引入构建顺序依赖性,因此这同时确保 c 库在 cpp 库之前构建。

但是,由于这依赖于 paho.mqtt.cpp 项目中使用的变量名称以及 paho.mqtt.c 项目中库目标的目标名称,这可能会在您更新时中断这些库之一到更新版本。