Linking against a static Qt5 build using CMake

The previous post was the script I use to statically build Qt5 on Windows, but the real fun part is then linking an application using that version ! Here is the part of the CMake script I’m using (as usual, for reference)

For the moment I only build a basic Widgets application. As I use more modules / functionalities, I’ll update the script. Also please note that the very beginning (finding Qt) is tailored to my development setups, so you might need to update the paths (or provide CMake with a valid QT_DIR option)

Edit 2019-10-17: Since I removed accessibility from my build script, WindowsUIAutomationSupport is no longer needed as a dependency of the Windows integration plugin.

Edit 2019-10-18: I created a repository to start collecting my various CMake helper scripts, and the following script is now hosted there (it’s also a lot cleaner, modular and easy to use) You can see it here: https://github.com/dcourtois/CMakeUtils/blob/master/Qt.cmake

#
# Set to On to use a statically built version of Qt
#
set (QT_STATIC On)
if (QT_STATIC)
    set (QT_STATIC_SUFFIX "static/lib/cmake")
endif ()

#
# Find Qt.
#
find_path (QT_DIR Qt5
    HINTS
        # User set directory
        "${QT_DIR}"

        # My dev directories, depending on which machine I'm on
        "C:/Development/Libs/Qt/5.13.1"
        "D:/Development/Libs/Qt/5.13.1"
        "~/Development/Libs/Qt/5.13.1"

    PATH_SUFFIXES
        # As for the dev dirs, these are the suffixes to the CMake folder on the various machines I own.
        # Note that those are the default subfolder when installing Qt from its official installer.
        "${QT_STATIC_SUFFIX}"
        "msvc2017_64/lib/cmake"
        "gcc_64/lib/cmake"
        "clang_64/lib/cmake"
)

#
# Log / Error
#
if (NOT QT_DIR)
    message (FATAL_ERROR "Couldn't find Qt. Use QT_DIR variable to point to a valid Qt install.")
else ()
    message (STATUS "Found Qt in '${QT_DIR}'")
endif ()

#
# Find our Qt components
#
set (CMAKE_PREFIX_PATH ${QT_DIR})
find_package (Qt5 5
    COMPONENTS
        Widgets
    REQUIRED
)

#
# When using static build, exported Qt targets miss a awefull lot of dependencies (on Windows
# at least, didn't check the other platforms) so to avoid bothering, patch Qt5::Widgets
#
if (QT_STATIC)

    #
    # Set a few paths
    #
    set (QT_LIB_DIR "${QT_DIR}/..")
    set (QT_PLUGIN_DIR "${QT_DIR}/../../plugins")

    #
    # Qt5::QWindowsIntegrationPlugin
    # note that this target somehow is already there even if we didn't search for it in the
    # find_package command. And since it's mandatory to run the most basic Widgets application
    # on Windows ...
    #
    if (TARGET Qt5::QWindowsIntegrationPlugin)

        # find additional components needed by the windows platform plugin
        find_package (Qt5 5
            COMPONENTS
                EventDispatcherSupport
                FontDatabaseSupport
                ThemeSupport
            REQUIRED
        )

        # configure direct dependencies of the plugin
        target_link_libraries(Qt5::QWindowsIntegrationPlugin
            INTERFACE
                # Qt targets
                Qt5::EventDispatcherSupport
                Qt5::FontDatabaseSupport
                Qt5::ThemeSupport

                # Windows libs
                Dwmapi.lib
                Imm32.lib
                Wtsapi32.lib

                # The following is needed if you want to use the Windows vista style plugin.
                # If you provide your own style or CSS, you can comment out the following libs.
                ${QT_PLUGIN_DIR}/styles/qwindowsvistastyle.lib
                UxTheme.lib
        )

    endif ()

    #
    # Qt5::FontDatabaseSupport
    #
    if (TARGET Qt5::FontDatabaseSupport)

        target_link_libraries(Qt5::FontDatabaseSupport
            INTERFACE
                # Qt libs
                ${QT_LIB_DIR}/qtfreetype.lib
        )

    endif ()

    #
    # Qt5::Gui
    #
    if (TARGET Qt5::Gui)

        target_link_libraries(Qt5::Gui
            INTERFACE
                # Qt targets
                Qt5::QWindowsIntegrationPlugin

                # Qt libs
                ${QT_LIB_DIR}/qtlibpng.lib
        )

    endif ()

    #
    # Qt5::Core
    #
    if (TARGET Qt5::Core)

        target_link_libraries(Qt5::Core
            INTERFACE
                # Qt libs
                ${QT_LIB_DIR}/qtpcre2.lib

                # Windows libs
                Netapi32.lib
                Ws2_32.lib
                UserEnv.lib
                Version.lib
                Winmm.lib
        )

        target_compile_definitions (Qt5::Core
            INTERFACE
                # Remove debug stuff from Qt
                $<$<CONFIG:Release>:QT_NO_DEBUG>
                $<$<CONFIG:Release>:QT_NO_DEBUG_OUTPUT>
                $<$<CONFIG:Release>:QT_NO_INFO_OUTPUT>
                $<$<CONFIG:Release>:QT_NO_WARNING_OUTPUT>

                # Since Qt was built in release, we need to match it on Windows
                _ITERATOR_DEBUG_LEVEL=0
        )

    endif ()

endif ()

So this needs to be used after your project has been defined, and then you’ll be able the just add Qt5::Widgets to your target’s link libraries, and it should correctly link everything needed to run your Widgets application.

You’ll still need to manually import plugins though, and since the platform integration one is mandatory, here is how to do it (put that in your main compilation unit or wherever you want)

#if defined(QT_STATIC)

// needed to import statically linked plugins
#include <QtPlugin>

// The minimum plugin needed is the platform integration. Without it, the application just crashes on startup.
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);

// This one is for the Windows 'Vista' theme. As I wrote in the CMake part, this is
// optional if you plan on using your own CSS or style.
Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin);

#endif