Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

qt6: fixes for cross compiling #15678

Closed
wants to merge 8 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 54 additions & 9 deletions recipes/qt/6.x.x/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import os
import textwrap

from conan import ConanFile
from conan import ConanFile, conan_version
from conan.tools.apple import is_apple_os
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout
from conan.tools.build import cross_building, check_min_cppstd, default_cppstd
from conan.tools.env import VirtualBuildEnv, VirtualRunEnv, Environment
from conan.tools.files import copy, get, replace_in_file, apply_conandata_patches, save, rm, rmdir, export_conandata_patches
from conan.tools.files import copy, get, replace_in_file, apply_conandata_patches, save, rm, rmdir, export_conandata_patches, rename
from conan.tools.gnu import PkgConfigDeps
from conan.tools.microsoft import msvc_runtime_flag, is_msvc
from conan.tools.scm import Version
Expand Down Expand Up @@ -210,6 +210,14 @@ def configure(self):
if self.options.multiconfiguration:
del self.settings.build_type

# If we are cross-compiling we need to enable these options primarily
#for the _native_ Qt build, because the builing Qt with the cross
# compiler requires the native Qt tools
if cross_building(self):
self.options["qt"].qttools = True
self.options["qt"].gui = True
self.options["qt"].widgets = True

Comment on lines +213 to +220
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# If we are cross-compiling we need to enable these options primarily
#for the _native_ Qt build, because the builing Qt with the cross
# compiler requires the native Qt tools
if cross_building(self):
self.options["qt"].qttools = True
self.options["qt"].gui = True
self.options["qt"].widgets = True

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, this doesn't work. When I remove these options from from configure(), then the native tool are not built and the cross building fails because 'lconvert' is not found.
This happens even when I explicitly request the options from the command line, e.g. with
conan create 6.x.x/conanfile.py 6.4.2@ -pr:b=default -pr:h=mingw-win64 -o=qt:gui=True -o=qt:qttools=True -o=qt:widgets=True --build=missing

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I don't understand your commit message in 30a0fb3

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, in the cross-compiling case, two instances of qt are built, because qt has itself as tool_requires.
The "tool_requires"-qt instance is built with the native tool-chain and requires the qttools submodule enabled.
When I set -o qt:qttools=True or -o:b qt:qttools=True on the command line of my conan create command it seems not to get through to the native qt build. Only the qtbase module is built in the native qt instance. But when I add self.options["qt"].qttools = True in configure(self) it works and the native qttools module gets compiled.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please report this issue on conan : https://github.com/conan-io/conan/issues/new?assignees=&labels=&template=bug.yml&title=%5Bbug%5D+SHORT+DESCRIPTION

Regarding this PR, you have to know that setting option values in configure(self) is not enough to be sure that the option values are correct. The problem is that some other package in the dependency graph could change the option afterwards (self.options["qt"].qttools = False for example). This is why you need to check the option values in validate(self), because at this point you are sure that the option values won't change any more.

def _enablemodule(mod):
if mod != "qtbase":
setattr(self.options, mod, True)
Expand Down Expand Up @@ -286,7 +294,12 @@ def validate(self):
raise ConanInvalidConfiguration("The 'with_wayland' option for the 'xkbcommon' package must be enabled when the 'qtwayland' option is enabled")

if cross_building(self):
raise ConanInvalidConfiguration("cross compiling qt 6 is not yet supported. Contributions are welcome")
if self.settings.os != "Windows":
raise ConanInvalidConfiguration("cross compiling qt 6 in this configuration is not yet supported. Contributions are welcome")
if Version(conan_version) <"1.58.0":
raise ConanInvalidConfiguration("for cross compiling qt 6 conan version 1.58 or later is required")
if not self.options["qt"].qttools or not self.options["qt"].gui or not self.options["qt"].widgets:
raise ConanInvalidConfiguration("When cross compiling, qttools and gui and widgets must be enabled")

if self.options.with_sqlite3 and not self.dependencies["sqlite3"].options.enable_column_metadata:
raise ConanInvalidConfiguration("sqlite3 option enable_column_metadata must be enabled for qt")
Expand Down Expand Up @@ -378,7 +391,7 @@ def build_requirements(self):
self.tool_requires("nodejs/18.15.0")
self.tool_requires("gperf/3.1")
# gperf, bison, flex, python >= 2.7.5 & < 3
if self.settings.os != "Windows":
if self._settings_build.os != "Windows":
self.tool_requires("bison/3.8.2")
self.tool_requires("flex/2.6.4")
else:
Expand All @@ -393,6 +406,11 @@ def generate(self):
ms = VirtualBuildEnv(self)
ms.generate()

if cross_building(self):
filecontents="include(qt_host_info)\n"
filecontents+="include(conan_qt_executables_variables)\n"
save(self, os.path.join(self.generators_folder, "Qt6HostInfoConfig.cmake"), filecontents)

tc = CMakeDeps(self)
tc.set_property("libdrm", "cmake_file_name", "Libdrm")
tc.set_property("libdrm::libdrm_libdrm", "cmake_target_name", "Libdrm::Libdrm")
Expand Down Expand Up @@ -556,12 +574,14 @@ def generate(self):
self.output.warning(f"host not supported: {self.settings.os} {self.settings.compiler} {self.settings.compiler.version} {self.settings.arch}")
if self.options.cross_compile:
tc.variables["QT_QMAKE_DEVICE_OPTIONS"] = f"CROSS_COMPILE={self.options.cross_compile}"
if cross_building(self):
tc.variables["Qt6HostInfo_DIR"] = self.generators_folder

tc.variables["FEATURE_pkg_config"] = "ON"
if self.settings.compiler == "gcc" and self.settings.build_type == "Debug" and not self.options.shared:
tc.variables["BUILD_WITH_PCH"]= "OFF" # disabling PCH to save disk space

if self.settings.os == "Windows":
if self._settings_build.os == "Windows":
tc.variables["HOST_PERL"] = self.dependencies.build["strawberryperl"].conf_info.get("user.strawberryperl:perl", check_type=str)
#"set(QT_EXTRA_INCLUDEPATHS ${CONAN_INCLUDE_DIRS})\n"
#"set(QT_EXTRA_DEFINES ${CONAN_DEFINES})\n"
Expand Down Expand Up @@ -596,6 +616,7 @@ def package_id(self):
def source(self):
destination = self.source_folder
if self.info.settings.os == "Windows":

# Don't use os.path.join, or it removes the \\?\ prefix, which enables long paths
destination = rf"\\?\{self.source_folder}"
get(self, **self.conan_data["sources"][self.version],
Expand Down Expand Up @@ -755,6 +776,9 @@ def package(self):
if module != "qtbase" and not self.options.get_safe(module):
rmdir(self, os.path.join(self.package_folder, "licenses", module))
rmdir(self, os.path.join(self.package_folder, "lib", "pkgconfig"))
qt6_host_info_cmake_orig = os.path.join(self.package_folder, "lib", "cmake", "Qt6HostInfo", "Qt6HostInfoConfig.cmake")
qt6_host_info_cmake = os.path.join(self.package_folder, "lib", "cmake", "Qt6Core", "qt_host_info.cmake")
rename(self, qt6_host_info_cmake_orig, qt6_host_info_cmake)
for mask in ["Find*.cmake", "*Config.cmake", "*-config.cmake"]:
rm(self, mask, self.package_folder, recursive=True)
rm(self, "*.la*", os.path.join(self.package_folder, "lib"), recursive=True)
Expand All @@ -769,14 +793,34 @@ def package(self):
rmdir(self, os.path.join(self.package_folder, "lib", "cmake", m))

extension = ""
if self.settings.os == "Windows":
if self._settings_build.os == "Windows":
extension = ".exe"

# hack: copy tools binaries from native build
if cross_building(self):
qt_tools = [ "tracegen", "cmake_automoc_parser", "moc",
"lupdate-pro", "lrelease-pro", "qtplugininfo", "qtpaths6",
"qtpaths", "qlalr", "qtattributionsscanner", "qvkgen", "rcc",
"windeployqt", "lconvert", "lrelease",
"lprodump", "uic", "lupdate", "qmake6", "qmake", "qtdiag6",
"qtdiag", "qhelpgenerator", "pixeltool", "linguist",
"assistant", "designer" ]
src_bin_dir = self.deps_env_info["qt"].PATH[0]
dst_bin_dir = os.path.join(self.package_folder, "bin")
for tool in qt_tools:
tool_exe = "{}{}".format(tool, extension)
if os.path.isfile(os.path.join(src_bin_dir, tool_exe)):
copy(self, tool_exe, src_bin_dir, dst_bin_dir)
else:
self.output.warn("Could not copy native tool {}".format(tool_exe))
copy(self, "*LICENSE*", self.source_folder, os.path.join(self.package_folder, "licenses"))

filecontents = "set(QT_CMAKE_EXPORT_NAMESPACE Qt6)\n"
ver = Version(self.version)
filecontents += f"set(QT_VERSION_MAJOR {ver.major})\n"
filecontents += f"set(QT_VERSION_MINOR {ver.minor})\n"
filecontents += f"set(QT_VERSION_PATCH {ver.patch})\n"
targets = ["moc", "rcc", "tracegen", "cmake_automoc_parser", "qlalr", "qmake"]
targets = ["moc", "rcc", "tracegen", "cmake_automoc_parser", "qlalr", "qmake", "qtpaths"]
if self.options.with_dbus:
targets.extend(["qdbuscpp2xml", "qdbusxml2cpp"])
if self.options.gui:
Expand Down Expand Up @@ -963,10 +1007,10 @@ def _create_plugin(pluginname, libname, plugintype, requires):

if self.settings.os == "Windows":
self.cpp_info.components["qtCore"].system_libs.append("authz")
self.cpp_info.components["qtCore"].system_libs.append("synchronization")
if is_msvc(self):
self.cpp_info.components["qtCore"].cxxflags.append("-permissive-")
self.cpp_info.components["qtCore"].cxxflags.append("-Zc:__cplusplus")
self.cpp_info.components["qtCore"].system_libs.append("synchronization")
self.cpp_info.components["qtCore"].system_libs.append("runtimeobject")
self.cpp_info.components["qtPlatform"].set_property("cmake_target_name", "Qt6::Platform")
self.cpp_info.components["qtPlatform"].names["cmake_find_package"] = "Platform"
Expand Down Expand Up @@ -1013,7 +1057,8 @@ def _create_plugin(pluginname, libname, plugintype, requires):
_create_plugin("QWindowsIntegrationPlugin", "qwindows", "platforms", ["Core", "Gui"])
_create_plugin("QWindowsVistaStylePlugin", "qwindowsvistastyle", "styles", ["Core", "Gui"])
self.cpp_info.components["qtQWindowsIntegrationPlugin"].system_libs = ["advapi32", "dwmapi", "gdi32", "imm32",
"ole32", "oleaut32", "shell32", "shlwapi", "user32", "winmm", "winspool", "wtsapi32"]
"ole32", "oleaut32", "shell32", "shlwapi", "user32", "winmm", "winspool", "wtsapi32",
"shcore", "d3d9", "runtimeobject" ]
elif self.settings.os == "Android":
_create_plugin("QAndroidIntegrationPlugin", "qtforandroid", "platforms", ["Core", "Gui"])
self.cpp_info.components["qtQAndroidIntegrationPlugin"].system_libs = ["android", "jnigraphics"]
Expand Down