Search code examples
qtcmakecmakelists-options

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


I've looked at similar questions but haven't found one for this basic scenario. I'm relatively new to CMake. I have a CMake-based Qt 5 project. It's a simple test application; during its build I want to build and statically link the open-source Paho MQTT C lib and the C++ wrapper for it. Those are two separate projects with their own CMakeLists.txt files.

Per Qt's default, it builds into a directory outside the source tree.

I've copied the source trees for these open-source libs under the parent directory of my project, and edited the top-level CMakeLists.txt file to add_subdirectory them.

I also added target_link_libraries. I can get the C lib by itself to build and link into the parent project, but if I add the C++ wrapper, the processing for the C++ wrapper complains that it can't find the C lib... which is true because it hasn't been built yet. A similar complaint for the C lib was solved by simply using Qt's "build all projects" menu item, but that doesn't work when the C++ wrapper lib is added. The wrapper's CMakeLists.txt files issues this:

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

And sure enough it doesn't exist because it has not been built when this preprocessing is done.

UPDATED: Here's my top-level CMakeLists.txt file, revised per Corristo's suggestion, which was successful in getting CMake to parse the entire hierarchy. The project now builds. I'm perplexed that the last two lines here result in an empty string, though. So does a similar attempt for the link directories.

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}")

enter image description here


Solution

  • It is a bit of a hack, but you can use the fact that the CMake find_* commands don't perform a search if the result variable is already set.

    From the paho.mqtt.cpp/src/CMakeLists.txt file we find that the output variable for find_library is called PAHO_MQTT_C_LIB and the include directory is expected to be in PAHO_MQTT_C_INC_DIR, which in the original CMakeLists.txt from version 1.0.0 (which seems to be the version you're using) is itself computed from PAHO_MQTT_C_PATH.

    Setting these two variables between the two add_subdirectory calls should then make this work:

    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)
    

    This makes use of the fact that target_link_libraries can be called both with library files (which is what the original paho.mqtt.cpp project expected) and with existing CMake targets (which is what we replaced it with). Linking to a CMake target also automatically introduces a build-order dependency, so this simultaneously ensures that the c library is built before the cpp library.

    However, since this relies on the names of the variables used in the paho.mqtt.cpp project as well as the target name of the library target in the paho.mqtt.c project this can break any time you update one of these libraries to a newer version.