Repository: conan-io/conan-center-index

Issue:
[package] qt/6.1.1: Platform plugins should not be linked unless Default_IF is true
with `cmake_find_multi`

on windows the following plugins are built (`QWindowsIntegrationPlugin` and `QWindowsDirect2DPlugin`)

both are platform plugins and duplicate symbols by default platform plugins are disabled but only enabled / added when the DEFAULT_IF argument is set to true (thus in this case since it's a windows platform only `QWindowsIntegrationPlugin` should be built/linked.



Discussion:
I don't understand what you are describing. qt recipe does not declare targets for  QWindowsIntegrationPlugin  nor QWindowsDirect2DPlugin currently, so they should not exist in the files generated by `cmake_find_package_multi`
Could you please give us more details so that we can try to reproduce the problem, and the full log ?
I think this is similar to https://codereview.qt-project.org/c/qt/qtbase/+/318857 and I think this might be an issue because we don't use the original Qt cmake files (not sure).

As an example both QWindowsIntegrationPlugin and QWindowsDirect2DPlugin contain:
```
lib/objects-Release/QWindowsIntegrationPlugin_resources_1/.rcc/qrc_openglblacklists.cpp.obj
lib/objects-Release/QWindowsDirect2DPlugin _resources_1/.rcc/qrc_openglblacklists.cpp.obj
```
which results in an redefinitions of symbols in those object files. You only encounter this when linking statically. Ref. the example log below.

```
[8/9] Linking CXX executable src\UI.exe
FAILED: src/UI.exe
cmd.exe /C "cd . && C:\Users\mjvk\.conan\data\cmake\3.20.4\_\_\package\0a420ff5c47119e668867cdb51baff0eca1fdb68\bin\cmake.exe -E vs_link_exe --intdir=src\CMakeFiles\UI.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\mt.exe --manifests  -- C:\PROGRA~2\MIB055~1\2019\BUILDT~1\VC\Tools\MSVC\1428~1.299\bin\Hostx64\x64\link.exe /nologo @CMakeFiles\UI.rsp  /out:src\UI.exe /implib:src\UI.lib /pdb:src\UI.pdb /version:0.0 /machine:x64 /INCREMENTAL:NO /subsystem:console  && cd ."
LINK: command "C:\PROGRA~2\MIB055~1\2019\BUILDT~1\VC\Tools\MSVC\1428~1.299\bin\Hostx64\x64\link.exe /nologo @CMakeFiles\UI.rsp /out:src\UI.exe /implib:src\UI.lib /pdb:src\UI.pdb /version:0.0 /machine:x64 /INCREMENTAL:NO /subsystem:console /MANIFEST /MANIFESTFILE:src\UI.exe.manifest" failed (exit code 1181) with the following output:
C:\.conan\10bb20\1\lib\objects-Release\Quick_resources_1\.rcc\qrc_scenegraph.cpp.obj : warning LNK4042: object specified more than once; extras ignored
C:\.conan\10bb20\1\lib\objects-Release\Gui_resources_1\.rcc\qrc_qpdf.cpp.obj : warning LNK4042: object specified more than once; extras ignored
qrc_openglblacklists.cpp.obj : error LNK2005: "int __cdecl qCleanupResources_openglblacklists(void)" (?qCleanupResources_openglblacklists@@YAHXZ) already defined in qrc_openglblacklists.cpp.obj
qrc_openglblacklists.cpp.obj : error LNK2005: "int __cdecl qInitResources_openglblacklists(void)" (?qInitResources_openglblacklists@@YAHXZ) already defined in qrc_openglblacklists.cpp.obj
qrc_cursors.cpp.obj : error LNK2005: "int __cdecl qCleanupResources_cursors(void)" (?qCleanupResources_cursors@@YAHXZ) already defined in qrc_cursors.cpp.obj
qrc_cursors.cpp.obj : error LNK2005: "int __cdecl qInitResources_cursors(void)" (?qInitResources_cursors@@YAHXZ) already defined in qrc_cursors.cpp.obj
```

Ref. https://bugreports.qt.io/browse/QTBUG-88080 for a similar issue.

Direct2d is enabled automatically if possible. I think it should just not be linked automatically.
```python
objects_dirs = glob.glob(os.path.join(self.package_folder, "lib", "objects-*/"))
for object_dir in objects_dirs:
            for m in os.listdir(object_dir):
                submodules_dir = os.path.join(object_dir, m)
                component = "qt" + m[:m.find("_")]
                for sub_dir in os.listdir(submodules_dir):
                    submodule_dir = os.path.join(submodules_dir, sub_dir)
                    obj_files = [os.path.join(submodule_dir, file) for file in os.listdir(submodule_dir)]
                    self.cpp_info.components[component].exelinkflags.extend(obj_files)
                    self.cpp_info.components[component].sharedlinkflags.extend(obj_files)
````

From the recipe, all the obj files under objects-* are added into linkflags include QWindowsIntegrationPlugin and QWindowsDirect2DPlugin.

In the generated conanbuildinfo.cmake, you could see:
```cmake
set(CONAN_SHARED_LINKER_FLAGS_QT_LIST "C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\PrintSupport_resources_1\\.rcc\\qrc_qprintdialog.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\PrintSupport_resources_2\\.rcc\\qrc_qprintdialog1.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\Widgets_resources_1\\.rcc\\qrc_qstyle.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\Widgets_resources_2\\.rcc\\qrc_qstyle1.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\Widgets_resources_3\\.rcc\\qrc_qmessagebox.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\Gui_resources_1\\.rcc\\qrc_qpdf.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\QWindowsDirect2DIntegrationPlugin_resources_1\\.rcc\\qrc_openglblacklists.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\QWindowsDirect2DIntegrationPlugin_resources_2\\.rcc\\qrc_cursors.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\QWindowsIntegrationPlugin_resources_1\\.rcc\\qrc_openglblacklists.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\QWindowsIntegrationPlugin_resources_2\\.rcc\\qrc_cursors.cpp.obj")
set(CONAN_EXE_LINKER_FLAGS_QT_LIST "C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\PrintSupport_resources_1\\.rcc\\qrc_qprintdialog.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\PrintSupport_resources_2\\.rcc\\qrc_qprintdialog1.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\Widgets_resources_1\\.rcc\\qrc_qstyle.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\Widgets_resources_2\\.rcc\\qrc_qstyle1.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\Widgets_resources_3\\.rcc\\qrc_qmessagebox.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\Gui_resources_1\\.rcc\\qrc_qpdf.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\QWindowsDirect2DIntegrationPlugin_resources_1\\.rcc\\qrc_openglblacklists.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\QWindowsDirect2DIntegrationPlugin_resources_2\\.rcc\\qrc_cursors.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\QWindowsIntegrationPlugin_resources_1\\.rcc\\qrc_openglblacklists.cpp.obj;C:\\.conan\\b1dddb\\1\\lib\\objects-Release\\QWindowsIntegrationPlugin_resources_2\\.rcc\\qrc_cursors.cpp.obj")
```

Resolving patch:
diff --git a/recipes/qt/5.x.x/conanfile.py b/recipes/qt/5.x.x/conanfile.py
--- a/recipes/qt/5.x.x/conanfile.py
+++ b/recipes/qt/5.x.x/conanfile.py
@@ -256,6 +256,8 @@ def _enablemodule(mod):
                 _enablemodule(module)
 
     def validate(self):
+        if self.settings.compiler.get_safe("cppstd"):
+            tools.check_min_cppstd(self, "11")
         if self.options.widgets and not self.options.gui:
             raise ConanInvalidConfiguration("using option qt:widgets without option qt:gui is not possible. "
                                             "You can either disable qt:widgets or enable qt:gui")
@@ -824,7 +826,11 @@ def _create_module(module, requires=[]):
             assert componentname not in self.cpp_info.components, "Module %s already present in self.cpp_info.components" % module
             self.cpp_info.components[componentname].names["cmake_find_package"] = module
             self.cpp_info.components[componentname].names["cmake_find_package_multi"] = module
-            self.cpp_info.components[componentname].libs = ["Qt5%s%s" % (module, libsuffix)]
+            if module.endswith("Private"):
+                libname = module[:-7]
+            else:
+                libname = module
+            self.cpp_info.components[componentname].libs = ["Qt5%s%s" % (libname, libsuffix)]
             self.cpp_info.components[componentname].includedirs = ["include", os.path.join("include", "Qt%s" % module)]
             self.cpp_info.components[componentname].defines = ["QT_%s_LIB" % module.upper()]
             if module != "Core" and "Core" not in requires:
@@ -883,6 +889,30 @@ def _create_plugin(pluginname, libname, type, requires):
             if self.options.with_libjpeg == "libjpeg":
                 gui_reqs.append("libjpeg::libjpeg")
             _create_module("Gui", gui_reqs)
+
+            if self.settings.os == "Windows":
+                _create_plugin("QWindowsIntegrationPlugin", "qwindows", "platforms", ["Core", "Gui"])
+                self.cpp_info.components["qtQWindowsIntegrationPlugin"].system_libs = ["advapi32", "dwmapi", "gdi32", "imm32",
+                    "ole32", "oleaut32", "shell32", "shlwapi", "user32", "winmm", "winspool", "wtsapi32"]
+            elif self.settings.os == "Android":
+                _create_plugin("QAndroidIntegrationPlugin", "qtforandroid", "platforms", ["Core", "Gui"])
+                self.cpp_info.components["qtQAndroidIntegrationPlugin"].system_libs = ["android", "jnigraphics"]
+            elif self.settings.os == "Macos":
+                _create_plugin("QCocoaIntegrationPlugin", "qcocoa", "platforms", ["Core", "Gui"])
+                self.cpp_info.components["QCocoaIntegrationPlugin"].frameworks = ["AppKit", "Carbon", "CoreServices", "CoreVideo",
+                    "IOKit", "IOSurface", "Metal", "QuartzCore"]
+            elif self.settings.os in ["iOS", "tvOS"]:
+                _create_plugin("QIOSIntegrationPlugin", "qios", "platforms", [])
+                self.cpp_info.components["QIOSIntegrationPlugin"].frameworks = ["AudioToolbox", "Foundation", "Metal",
+                    "QuartzCore", "UIKit"]
+            elif self.settings.os == "watchOS":
+                _create_plugin("QMinimalIntegrationPlugin", "qminimal", "platforms", [])
+            elif self.settings.os == "Emscripten":
+                _create_plugin("QWasmIntegrationPlugin", "qwasm", "platforms", ["Core", "Gui"])
+            else:
+                _create_module("XcbQpaPrivate", ["xkbcommon::libxkbcommon-x11", "xorg::xorg"])
+                _create_plugin("QXcbIntegrationPlugin", "qxcb", "platforms", ["Core", "Gui", "XcbQpaPrivate"])
+
         if self.options.with_sqlite3:
             _create_plugin("QSQLiteDriverPlugin", "qsqlite", "sqldrivers", ["sqlite3::sqlite3"])
         if self.options.with_pq:
@@ -972,7 +1002,10 @@ def _create_plugin(pluginname, libname, type, requires):
             _create_module("WebChannel", ["Qml"])
 
         if self.options.qtwebengine:
-            _create_module("WebEngineCore", ["Gui", "Quick", "WebChannel", "Positioning", "expat::expat", "opus::libopus"])
+            webenginereqs = ["Gui", "Quick", "WebChannel", "Positioning"]
+            if self.settings.os == "Linux":
+                webenginereqs.extend(["expat::expat", "opus::libopus"])
+            _create_module("WebEngineCore", webenginereqs)
             _create_module("WebEngine", ["WebEngineCore"])
             _create_module("WebEngineWidgets", ["WebEngineCore", "Quick", "PrintSupport", "Widgets", "Gui", "Network"])
 
@@ -1135,8 +1168,10 @@ def _create_plugin(pluginname, libname, type, requires):
         objects_dirs = glob.glob(os.path.join(self.package_folder, "lib", "objects-*/"))
         for object_dir in objects_dirs:
             for m in os.listdir(object_dir):
-                submodules_dir = os.path.join(object_dir, m)
                 component = "qt" + m[:m.find("_")]
+                if component not in self.cpp_info.components:
+                    continue
+                submodules_dir = os.path.join(object_dir, m)
                 for sub_dir in os.listdir(submodules_dir):
                     submodule_dir = os.path.join(submodules_dir, sub_dir)
                     obj_files = [os.path.join(submodule_dir, file) for file in os.listdir(submodule_dir)]


Test patch:
diff --git a/recipes/qt/5.x.x/test_package/test_package.cpp b/recipes/qt/5.x.x/test_package/test_package.cpp
--- a/recipes/qt/5.x.x/test_package/test_package.cpp
+++ b/recipes/qt/5.x.x/test_package/test_package.cpp
@@ -10,6 +10,11 @@
 #include <QDomText>
 #include <QSqlDatabase>
 
+void f()
+{
+    qDebug() << "inside f";
+}
+
 int main(int argc, char *argv[]){
     QCoreApplication app(argc, argv);
     QCoreApplication::setApplicationName("Application Example");
@@ -34,7 +39,8 @@ int main(int argc, char *argv[]){
 
     QSqlDatabase sqlTester;
 
-    QFuture<void> future = QtConcurrent::run([](){});
+    QFuture<void> future = QtConcurrent::run(::f);
+    future.waitForFinished();
 
     QDomText xmlTester;