CMake: Resurrect test_import_plugins cmake build tests

Now that we run tests for static Qt builds in the CI, it makes sense
to restore the CMake build tests that check that static plugin
importing works correctly.

Resurrect the previously commented out test_import_plugins project and
port the mockplugins qmake projects to CMake.

mockplugins is a CMake project that uses the internal Qt CMake API to
build and install some Qt modules and plugins.

test_import_plugins depends on that test (via a CMake fixture) to
build public projects that use those plugins.

The installation of the mockplugins modules pollutes the Qt install
prefix, but in the CI that only happens on the test VM, which means
the release packages are not affected.

Locally on a developer machine the Qt install path will be polluted,
but it's not that much of a big deal. We could try and address that in
a future change by using the QT_ADDITIONAL_PACKAGES_PREFIX_PATH
functionality added for Conan to allow the installation of Qt packages
into a non-standard prefix.

Task-number: QTBUG-87580
Task-number: QTBUG-92933
Change-Id: I0841064a60a5ffba5118640d3197527a38ba6c30
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
This commit is contained in:
Alexandru Croitor 2021-05-06 16:38:53 +02:00
parent 22a992cb12
commit 3a62f9e0c9
18 changed files with 200 additions and 62 deletions

View File

@ -803,10 +803,13 @@ function(qt6_import_plugins target)
# Check if passed plugin target name is a version-less one, and make a version-full
# one.
set_property(TARGET "${target}" APPEND PROPERTY "QT_PLUGINS_${_current_type}" "${_arg}")
_qt_get_plugin_name_with_version("${_arg}" qt_plugin_with_version)
if(TARGET "${_arg}" OR TARGET "${qt_plugin_with_version}")
set_property(TARGET "${target}" APPEND PROPERTY "QT_PLUGINS_${_current_type}" "${_arg}")
else()
# TODO: Do we really need this check? We didn't have it in Qt5, and plugin targets
# wrapped in genexes end up causing warnings, but we explicitly use GENEX_EVAL to
# support them.
if(NOT TARGET "${_arg}" AND NOT TARGET "${qt_plugin_with_version}")
message("Warning: plug-in ${_arg} is not known to the current Qt installation.")
endif()
endif()

View File

@ -221,8 +221,30 @@ if(NOT NO_WIDGETS)
_qt_internal_test_expect_pass(test_QTBUG-63422)
endif()
# FIXME: Needs porting of the qmake .pro files to create the modules and plugins in Qt6 CMake land.
# _qt_internal_test_expect_pass(test_import_plugins BINARY ${CMAKE_CTEST_COMMAND})
# Find main Qt installation location and bin dir.
if(QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX)
set(qt_install_prefix "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}")
elseif(QT6_INSTALL_PREFIX)
set(qt_install_prefix "${QT6_INSTALL_PREFIX}")
endif()
if(INSTALL_BINDIR)
set(qt_install_bin_dir "${INSTALL_BINDIR}")
elseif(QT6_INSTALL_BINS)
set(qt_install_bin_dir "${QT6_INSTALL_BINS}")
endif()
# Test building and installing a few dummy Qt modules and plugins.
_qt_internal_test_expect_pass(mockplugins
BINARY "${CMAKE_COMMAND}"
BINARY_ARGS
"-DQT_BUILD_DIR=${CMAKE_CURRENT_BINARY_DIR}/mockplugins"
-P "${qt_install_prefix}/${qt_install_bin_dir}/qt-cmake-private-install.cmake")
set_tests_properties(mockplugins PROPERTIES FIXTURES_SETUP build_mockplugins)
# Test importing the plugins built in the project above.
_qt_internal_test_expect_pass(test_import_plugins BINARY ${CMAKE_CTEST_COMMAND} BINARY_ARGS -V)
set_tests_properties(test_import_plugins PROPERTIES FIXTURES_REQUIRED build_mockplugins)
_qt_internal_test_expect_pass(test_versionless_targets)
_qt_internal_test_expect_pass(test_add_resources_binary_generated

View File

@ -0,0 +1 @@
set(QT_REPO_MODULE_VERSION "6.2.0")

View File

@ -0,0 +1,32 @@
# Generated from qtsvg.pro.
cmake_minimum_required(VERSION 3.15.0)
include(.cmake.conf)
project(QtMockPlugins
VERSION "${QT_REPO_MODULE_VERSION}"
DESCRIPTION "Qt MockPlugins Libraries"
HOMEPAGE_URL "https://qt.io/"
LANGUAGES CXX C
)
# Make sure we only use latest private CMake API, aka no compatibility wrappers.
set(QT_NO_INTERNAL_COMPATIBILITY_FUNCTIONS TRUE)
find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals Core)
find_package(Qt6 ${PROJECT_VERSION} QUIET CONFIG OPTIONAL_COMPONENTS Gui Widgets Xml)
qt_build_repo_begin()
add_subdirectory(mockplugins1)
add_subdirectory(mockplugins2)
add_subdirectory(mockplugins3)
add_subdirectory(mock1plugin)
add_subdirectory(mock2plugin)
add_subdirectory(mock3plugin)
add_subdirectory(mock4plugin)
add_subdirectory(mock5plugin)
add_subdirectory(mock6plugin)
qt_build_repo_end()

View File

@ -0,0 +1,9 @@
qt_internal_add_plugin(QMock1Plugin
CLASS_NAME QMock1Plugin
TYPE mockplugin
SOURCES
qmock1plugin.cpp qmock1plugin.h
PUBLIC_LIBRARIES
Qt::Core
Qt::MockPlugins1
)

View File

@ -0,0 +1,9 @@
qt_internal_add_plugin(QMock2Plugin
CLASS_NAME QMock2Plugin
TYPE mockplugin
SOURCES
qmock2plugin.cpp qmock2plugin.h
PUBLIC_LIBRARIES
Qt::Core
Qt::MockPlugins1
)

View File

@ -0,0 +1,9 @@
qt_internal_add_plugin(QMock3Plugin
CLASS_NAME QMock3Plugin
TYPE mockplugin
SOURCES
qmock3plugin.cpp qmock3plugin.h
PUBLIC_LIBRARIES
Qt::Core
Qt::MockPlugins1
)

View File

@ -0,0 +1,10 @@
qt_internal_add_plugin(QMock4Plugin
CLASS_NAME QMock4Plugin
TYPE mockplugin
DEFAULT_IF FALSE
SOURCES
qmock4plugin.cpp qmock4plugin.h
PUBLIC_LIBRARIES
Qt::Core
Qt::MockPlugins1
)

View File

@ -0,0 +1,10 @@
qt_internal_add_plugin(QMock5Plugin
CLASS_NAME QMock5Plugin
TYPE mockplugin
DEFAULT_IF FALSE
SOURCES
qmock5plugin.cpp qmock5plugin.h
PUBLIC_LIBRARIES
Qt::Core
Qt::MockPlugins3
)

View File

@ -0,0 +1,9 @@
qt_internal_add_plugin(QMock6Plugin
CLASS_NAME QMock6Plugin
TYPE mockauxplugin
SOURCES
qmock6plugin.cpp qmock6plugin.h
PUBLIC_LIBRARIES
Qt::Core
Qt::MockPlugins3
)

View File

@ -0,0 +1,9 @@
qt_internal_add_module(MockPlugins1
PLUGIN_TYPES mockplugin
SOURCES
fake.cpp
LIBRARIES
Qt::CorePrivate
PUBLIC_LIBRARIES
Qt::Core
)

View File

@ -0,0 +1,2 @@
# This is needed so that MODULE_PLUGIN_TYPES property is set on the exported target.
# Fun times.

View File

@ -0,0 +1,8 @@
qt_internal_add_module(MockPlugins2
SOURCES
fake.cpp
LIBRARIES
Qt::CorePrivate
PUBLIC_LIBRARIES
Qt::Core
)

View File

@ -0,0 +1,9 @@
qt_internal_add_module(MockPlugins3
PLUGIN_TYPES mockauxplugin
SOURCES
fake.cpp
LIBRARIES
Qt::CorePrivate
PUBLIC_LIBRARIES
Qt::Core
)

View File

@ -0,0 +1,2 @@
# This is needed so that MODULE_PLUGIN_TYPES property is set on the exported target.
# Fun times.

View File

@ -1,4 +1,3 @@
cmake_minimum_required(VERSION 3.1)
project(import_plugins_advanced)
@ -6,26 +5,9 @@ enable_testing()
set(CMAKE_INCLUDE_CURRENT_DIR ON)
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/FindPackageHints.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/FindPackageHints.cmake")
endif()
# Need to find Qt5Core explicitly because the MockPlugins1 and MockPlugins2 config files
# are in a different directory (the source dir) when doing a standalone tests build,
# whereas Core is in the installed directory, and due to NO_DEFAULT_PATH being used
# for the Core dependency call in Qt5MockPlugins, Core would not be found in the source
# dir.
find_package(Qt5 COMPONENTS Core REQUIRED HINTS ${Qt5Tests_PREFIX_PATH})
get_target_property(qt_is_static Qt5::Core TYPE)
# For a similar reason, we need to find the MockPlugins packages not via COMPONENTS argument,
# but directly, because the location of Qt5Config.cmake is in the installed dir, while
# the MockPlugins are in the source dir, and Qt5Config only looks for packages relative
# to its own location.
# The packages are still successfuly found, because the CMAKE_PREFIX_PATH populated by qmake
# contains both the installed Qt dir, and the Qt source dir.
find_package(Qt5MockPlugins1 REQUIRED HINTS ${Qt5Tests_PREFIX_PATH})
find_package(Qt5MockPlugins2 REQUIRED HINTS ${Qt5Tests_PREFIX_PATH})
find_package(Qt6 COMPONENTS REQUIRED MockPlugins1 MockPlugins2)
# MockPlugins3 is automatically find_dependency'd by QMock5Plugin which depends on MockPlugins3.
# QMock5Plugin itself is loaded by QtMockPlugins1Plugins.cmake.
function(create_test_executable TARGET_NAME)
set(CHECK_FILE ${CMAKE_BINARY_DIR}/${TARGET_NAME}_check.cpp)
@ -37,75 +19,87 @@ function(create_test_executable TARGET_NAME)
configure_file("${CMAKE_SOURCE_DIR}/check.cpp.in" ${CHECK_FILE})
add_executable(${TARGET_NAME} main.cpp ${CHECK_FILE})
target_link_libraries(${TARGET_NAME} Qt5::MockPlugins1)
target_link_libraries(${TARGET_NAME} PRIVATE Qt6::MockPlugins1)
add_test(test_${TARGET_NAME} ${TARGET_NAME})
endfunction()
create_test_executable(default QMock1Plugin QMock2Plugin)
# No call to qt5_import_plugins() for the default
# No call to qt_import_plugins() for the default case.
create_test_executable(default
QMock1Plugin QMock2Plugin
# TODO This test is known to fail because CMake currently doesn't have a way to
# implement its own equivalent of the PLUGIN_EXTENDS mechanism at generate-
# time (meaning a library only gets linked if a set of other libraries are
# *also* linked.) CMake 3.14 or beyond may have such a mechanism, but until
# then, this test is expected to fail, because QMock3Plugin is not being
# linked even though MockPlugins2 is present.
create_test_executable(default_link QMock1Plugin QMock2Plugin QMock3Plugin)
target_link_libraries(default_link Qt5::MockPlugins2)
set_property(TEST test_default_link PROPERTY DISABLED 1)
# No call to qt5_import_plugins() for the default
QMock3Plugin # TODO: Should not be linked based on .pro file, see QTBUG-93501
)
# No call to qt_import_plugins() for the default_link case.
create_test_executable(default_link QMock1Plugin QMock2Plugin
# TODO: in qmake QMock3Plugin should only be linked if the executable depends on MockPlugins2
# module (based on .pro file PLUGIN_EXTENDS). Here it's accidentally linked because
# we're missing PLUGIN_EXTENDS information in CMake land. Thus it's considered
# a default plugin which is linked regardless of whether MockPlugins2 is linked.
# It's possible the qmake behavior is also wrong, because the qmake qt5 test seems to
# expect to link the plugin if both MockPlugins1 AND MockPlugins2 are linked, but qt.pf
# suggests that MockPlugins1 OR MockPlugins2 is sufficient to link the plugin, not both.
# See QTBUG-93501
QMock3Plugin
)
target_link_libraries(default_link PRIVATE Qt6::MockPlugins2)
create_test_executable(manual QMock1Plugin QMock2Plugin QMock3Plugin QMock4Plugin)
qt5_import_plugins(manual
INCLUDE Qt5::QMock3Plugin Qt5::QMock4Plugin
qt_import_plugins(manual
INCLUDE Qt6::QMock3Plugin Qt6::QMock4Plugin
)
create_test_executable(manual_genex QMock1Plugin QMock2Plugin QMock3Plugin)
qt5_import_plugins(manual_genex
INCLUDE $<1:Qt5::QMock3Plugin> $<0:Qt5::QMock4Plugin>
qt_import_plugins(manual_genex
INCLUDE $<1:Qt6::QMock3Plugin> $<0:Qt6::QMock4Plugin>
)
create_test_executable(blacklist QMock1Plugin)
qt5_import_plugins(blacklist
EXCLUDE Qt5::QMock2Plugin Qt5::QMock3Plugin
qt_import_plugins(blacklist
EXCLUDE Qt6::QMock2Plugin Qt6::QMock3Plugin
)
create_test_executable(blacklist_genex QMock1Plugin)
qt5_import_plugins(blacklist_genex
EXCLUDE $<1:Qt5::QMock2Plugin> $<1:Qt5::QMock3Plugin> $<0:Qt5::QMock1Plugin>
qt_import_plugins(blacklist_genex
EXCLUDE $<1:Qt6::QMock2Plugin> $<1:Qt6::QMock3Plugin> $<0:Qt6::QMock1Plugin>
)
create_test_executable(override QMock3Plugin QMock4Plugin)
qt5_import_plugins(override
INCLUDE_BY_TYPE mockplugin Qt5::QMock3Plugin Qt5::QMock4Plugin
qt_import_plugins(override
INCLUDE_BY_TYPE mockplugin Qt6::QMock3Plugin Qt6::QMock4Plugin
)
create_test_executable(override_genex QMock3Plugin)
qt5_import_plugins(override_genex
INCLUDE_BY_TYPE mockplugin $<1:Qt5::QMock3Plugin> $<0:Qt5::QMock4Plugin>
qt_import_plugins(override_genex
INCLUDE_BY_TYPE mockplugin $<1:Qt6::QMock3Plugin> $<0:Qt6::QMock4Plugin>
)
get_target_property(prop_plugs override_genex QT_PLUGINS)
get_target_property(prop_types override_genex QT_PLUGINS_mockplugin)
create_test_executable(override_mix QMock2Plugin QMock3Plugin)
qt5_import_plugins(override_mix
INCLUDE Qt5::QMock2Plugin
INCLUDE_BY_TYPE mockplugin Qt5::QMock3Plugin
qt_import_plugins(override_mix
INCLUDE Qt6::QMock2Plugin
INCLUDE_BY_TYPE mockplugin Qt6::QMock3Plugin
)
if(NOT WIN32)
# Compiling an empty static array fails on Windows.
create_test_executable(none)
qt5_import_plugins(none
qt_import_plugins(none
EXCLUDE_BY_TYPE mockplugin
)
endif()
create_test_executable(none_mix QMock3Plugin QMock4Plugin)
qt5_import_plugins(none_mix
INCLUDE Qt5::QMock3Plugin Qt5::QMock4Plugin
qt_import_plugins(none_mix
INCLUDE Qt6::QMock3Plugin Qt6::QMock4Plugin
EXCLUDE_BY_TYPE mockplugin
)
# QMock5Plugin links against the Qt::MockPlugins3 module, which provides the default plugin
# QMock6Plugin which is why it is pulled in.
create_test_executable(recursive QMock5Plugin QMock6Plugin)
qt5_import_plugins(recursive
INCLUDE_BY_TYPE mockplugin Qt5::QMock5Plugin
qt_import_plugins(recursive
INCLUDE_BY_TYPE mockplugin Qt6::QMock5Plugin
)

View File

@ -5,4 +5,4 @@ QString expectedPlugins[] = {
@EXPECTED_PLUGINS@
};
std::size_t numExpectedPlugins = sizeof(expectedPlugins) / sizeof(numExpectedPlugins);
std::size_t numExpectedPlugins = sizeof(expectedPlugins) / sizeof(*expectedPlugins);

View File

@ -77,7 +77,7 @@ int main(int argc, char **argv)
std::cerr << "Loaded plugins do not match what was expected!" << std::endl
<< "Expected plugins:" << std::endl;
QList<QString> expectedPluginList = expectedPluginSet.toList();
QList<QString> expectedPluginList = expectedPluginSet.values();
expectedPluginList.sort();
for (QString plugin : expectedPluginList) {
std::cerr << (actualPluginSet.contains(plugin) ? " " : "- ")
@ -86,7 +86,7 @@ int main(int argc, char **argv)
std::cerr << std::endl << "Actual plugins:" << std::endl;
QList<QString> actualPluginList = actualPluginSet.toList();
QList<QString> actualPluginList = actualPluginSet.values();
actualPluginList.sort();
for (QString plugin : actualPluginList) {
std::cerr << (expectedPluginSet.contains(plugin) ? " " : "+ ")