Files
conan-build/recipes/qt/5.x.x/conanfile.py
2025-02-25 14:03:04 +03:00

1028 lines
49 KiB
Python

from conan import ConanFile, conan_version
from conan.errors import ConanException, ConanInvalidConfiguration
from conan.tools.android import android_abi
from conan.tools.apple import is_apple_os
from conan.tools.build import build_jobs, check_min_cppstd, cross_building
from conan.tools.env import Environment, VirtualBuildEnv, VirtualRunEnv
from conan.tools.files import chdir, copy, get, load, replace_in_file, rm, rmdir, save, export_conandata_patches, apply_conandata_patches
from conan.tools.gnu import PkgConfigDeps
from conan.tools.microsoft import is_msvc, msvc_runtime_flag, is_msvc_static_runtime, VCVars
from conan.tools.scm import Version
import configparser
import glob
from io import StringIO
import itertools
import os
import textwrap
import shutil
required_conan_version = ">=1.60.0 <2 || >=2.0.5"
class QtConan(ConanFile):
_submodules = ["qtsvg", "qtdeclarative", "qtactiveqt", "qtmultimedia", "qttools", "qtxmlpatterns", "qtwayland", "qtwebchannel", "qtscript", "qtdoc",
"qttranslations", "qtlocation", "qtsensors", "qtconnectivity",
"qt3d", "qtimageformats", "qtgraphicaleffects", "qtquickcontrols", "qtserialbus", "qtserialport", "qtx11extras",
"qtmacextras", "qtandroidextras", "qtwinextras", "qtwebengine", "qtwebsockets", "qtwebview",
"qtquickcontrols2", "qtpurchasing", "qtcharts", "qtdatavis3d", "qtvirtualkeyboard", "qtgamepad", "qtscxml",
"qtspeech", "qtnetworkauth", "qtremoteobjects", "qtwebglplugin", "qtlottie", "qtquicktimeline", "qtquick3d",
"qtknx", "qtmqtt", "qtcoap", "qtopcua"]
_module_statuses = ["essential", "addon", "deprecated", "preview"]
name = "qt"
description = "Qt is a cross-platform framework for graphical user interfaces."
topics = ("ui", "framework")
url = "https://github.com/conan-io/conan-center-index"
homepage = "https://www.qt.io"
license = "LGPL-3.0-only"
package_type = "library"
settings = "os", "arch", "compiler", "build_type"
options = {
"shared": [True, False],
"commercial": [True, False],
"opengl": ["no", "es2", "desktop", "dynamic"],
"with_vulkan": [True, False],
"openssl": [True, False],
"with_pcre2": [True, False],
"with_glib": [True, False],
# "with_libiconv": [True, False], # QTBUG-84708 Qt tests failure "invalid conversion from const char** to char**"
"with_doubleconversion": [True, False],
"with_freetype": [True, False],
"with_fontconfig": [True, False],
"with_icu": [True, False],
"with_harfbuzz": [True, False],
"with_libjpeg": ["libjpeg", "libjpeg-turbo", False],
"with_libpng": [True, False],
"with_sqlite3": [True, False],
"with_mysql": [True, False],
"with_pq": [True, False],
"with_odbc": [True, False],
"with_libalsa": [True, False],
"with_openal": [True, False],
"with_zstd": [True, False],
"with_gstreamer": [True, False],
"with_pulseaudio": [True, False],
"with_dbus": [True, False],
"with_gssapi": [True, False],
"with_atspi": [True, False],
"with_md4c": [True, False],
"with_x11": [True, False],
"gui": [True, False],
"widgets": [True, False],
"android_sdk": [None, "ANY"],
"device": [None, "ANY"],
"cross_compile": [None, "ANY"],
"sysroot": [None, "ANY"],
"config": [None, "ANY"],
"multiconfiguration": [True, False]
}
options.update({module: [True, False] for module in _submodules})
options.update({f"{status}_modules": [True, False] for status in _module_statuses})
default_options = {
"shared": False,
"commercial": False,
"opengl": "dynamic",
"with_vulkan": False,
"openssl": True,
"with_pcre2": True,
"with_glib": False,
# "with_libiconv": True, # QTBUG-84708
"with_doubleconversion": True,
"with_freetype": True,
"with_fontconfig": True,
"with_icu": True,
"with_harfbuzz": False,
"with_libjpeg": "libjpeg-turbo",
"with_libpng": True,
"with_sqlite3": True,
"with_mysql": True,
"with_pq": True,
"with_odbc": True,
"with_libalsa": False,
"with_openal": True,
"with_zstd": True,
"with_gstreamer": False,
"with_pulseaudio": False,
"with_dbus": False,
"with_gssapi": False,
"with_atspi": False,
"with_md4c": True,
"with_x11": True,
"gui": True,
"widgets": True,
"android_sdk": None,
"device": None,
"cross_compile": None,
"sysroot": None,
"config": None,
"multiconfiguration": False,
}
# essential_modules, addon_modules, deprecated_modules, preview_modules:
# these are only provided for convenience, set to False by default
default_options.update({f"{status}_modules": False for status in _module_statuses})
no_copy_source = True
short_paths = True
@property
def _settings_build(self):
return getattr(self, "settings_build", self.settings)
def export(self):
copy(self, f"qtmodules{self.version}.conf", self.recipe_folder, self.export_folder)
def export_sources(self):
export_conandata_patches(self)
def validate_build(self):
if self.options.qtwebengine:
# Check if a valid python2 is available in PATH or it will failflex
# Start by checking if python2 can be found
python_exe = shutil.which("python2")
if not python_exe:
# Fall back on regular python
python_exe = shutil.which("python")
if not python_exe:
msg = ("Python2 must be available in PATH "
"in order to build Qt WebEngine")
raise ConanInvalidConfiguration(msg)
# In any case, check its actual version for compatibility
command_output = StringIO()
cmd_v = f"\"{python_exe}\" -c \"import platform;print(platform.python_version())\""
self.run(cmd_v, command_output)
verstr = command_output.getvalue().strip()
version = Version(verstr)
# >= 2.7.5 & < 3
v_min = "2.7.5"
v_max = "3.0.0"
if (version >= v_min) and (version < v_max):
msg = ("Found valid Python 2 required for QtWebengine:"
f" version={verstr}, path={python_exe}")
self.output.success(msg)
else:
msg = (f"Found Python 2 in path, but with invalid version {verstr}"
f" (QtWebEngine requires >= {v_min} & < {v_max})\n"
"If you have both Python 2 and 3 installed, copy the python 2 executable to"
"python2(.exe)")
raise ConanInvalidConfiguration(msg)
def config_options(self):
if self.settings.os not in ["Linux", "FreeBSD"]:
del self.options.with_icu
del self.options.with_fontconfig
del self.options.with_libalsa
del self.options.with_x11
del self.options.qtx11extras
if self.settings.compiler == "apple-clang":
if Version(self.settings.compiler.version) < "10.0":
raise ConanInvalidConfiguration("Old versions of apple sdk are not supported by Qt (QTBUG-76777)")
if self.settings.compiler in ["gcc", "clang"]:
if Version(self.settings.compiler.version) < "5.0":
raise ConanInvalidConfiguration("qt 5.15.X does not support GCC or clang before 5.0")
if self.settings.compiler in ["gcc", "clang"] and Version(self.settings.compiler.version) < "5.3":
del self.options.with_mysql
if self.settings.os == "Windows":
self.options.with_mysql = False
if not cross_building(self):
self.options.opengl = "dynamic"
del self.options.with_gssapi
if self.settings.os != "Linux":
self.options.qtwayland = False
self.options.with_atspi = False
if self.settings.os != "Windows":
del self.options.qtwinextras
del self.options.qtactiveqt
if self.settings.os != "Macos":
del self.options.qtmacextras
if self.settings.os != "Android":
del self.options.android_sdk
def _debug_output(self, message):
if Version(conan_version) >= "2":
self.output.debug(message)
def configure(self):
# if self.settings.os != "Linux":
# self.options.with_libiconv = False # QTBUG-84708
if not self.options.gui:
self.options.rm_safe("opengl")
self.options.rm_safe("with_vulkan")
self.options.rm_safe("with_freetype")
self.options.rm_safe("with_fontconfig")
self.options.rm_safe("with_harfbuzz")
self.options.rm_safe("with_libjpeg")
self.options.rm_safe("with_libpng")
self.options.rm_safe("with_md4c")
self.options.rm_safe("with_x11")
if not self.options.with_dbus:
self.options.rm_safe("with_atspi")
if self.options.multiconfiguration:
del self.settings.build_type
config = configparser.ConfigParser()
config.read(os.path.join(self.recipe_folder, f"qtmodules{self.version}.conf"))
submodules_tree = {}
assert config.sections(), f"no qtmodules.conf file for version {self.version}"
for s in config.sections():
section = str(s)
assert section.startswith("submodule ")
assert section.count('"') == 2
modulename = section[section.find('"') + 1: section.rfind('"')]
status = str(config.get(section, "status"))
if status not in ("obsolete", "ignore"):
if status not in self._module_statuses:
raise ConanException(f"module {modulename} has status {status} which is not in self._module_statuses {self._module_statuses}")
submodules_tree[modulename] = {"status": status,
"path": str(config.get(section, "path")), "depends": []}
if config.has_option(section, "depends"):
submodules_tree[modulename]["depends"] = [str(i) for i in config.get(section, "depends").split()]
for m in submodules_tree:
assert m in ["qtbase", "qtqa", "qtrepotools"] or m in self._submodules, "module %s is not present in recipe options : (%s)" % (m, ",".join(self._submodules))
for module in self._submodules:
if module not in submodules_tree:
self._debug_output(f"qt5: removing {module} from options as it is not an option for this version, or it is ignored or obsolete")
self.options.rm_safe(module)
# Requested modules:
# - any module for non-removed options that have 'True' value
# - any enabled via `xxx_modules` that does not have a 'False' value
# Note that at this point, the submodule options dont have a value unless one is given externally
# to the recipe (e.g. via the command line, a profile, or a consumer)
requested_modules = set([module for module in self._submodules if self.options.get_safe(module)])
for module in [m for m in self._submodules if m in submodules_tree]:
status = submodules_tree[module]['status']
is_disabled = self.options.get_safe(module) == False
if self.options.get_safe(f"{status}_modules"):
if not is_disabled:
requested_modules.add(module)
else:
self.output.warning(f"qt5: {module} requested because {status}_modules=True"
f" but it has been explicitly disabled with {module}=False")
self.output.info(f"qt5: requested modules {list(requested_modules)}")
required_modules = {}
for module in requested_modules:
deps = submodules_tree[module]["depends"]
for dep in deps:
required_modules.setdefault(dep,[]).append(module)
required_but_disabled = [m for m in required_modules.keys() if self.options.get_safe(m) == False]
if required_modules:
self._debug_output(f"qt5: required_modules modules {list(required_modules.keys())}")
if required_but_disabled:
required_by = set()
for m in required_but_disabled:
required_by.update(required_modules[m])
raise ConanInvalidConfiguration(f"Modules {required_but_disabled} are explicitly disabled, "
f"but are required by {list(required_by)}, enabled by other options")
enabled_modules = requested_modules.union(set(required_modules.keys()))
enabled_modules.discard("qtbase")
for module in list(enabled_modules):
setattr(self.options, module, True)
for module in self._submodules:
if module in self.options and not self.options.get_safe(module):
setattr(self.options, module, False)
if not self.options.qtmultimedia:
self.options.rm_safe("with_libalsa")
del self.options.with_openal
del self.options.with_gstreamer
del self.options.with_pulseaudio
if self.settings.os in ("FreeBSD", "Linux"):
if self.options.qtwebengine:
self.options.with_fontconfig = True
for status in self._module_statuses:
# These are convenience only, should not affect package_id
option_name = f"{status}_modules"
self._debug_output(f"qt5 removing convenience option: {option_name},"
f" see individual module options")
self.options.rm_safe(option_name)
for option in self.options.items():
self._debug_output(f"qt5 option {option[0]}={option[1]}")
def validate(self):
if self.settings.compiler.get_safe("cppstd"):
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")
if self.options.qtwebengine:
if not self.options.shared:
raise ConanInvalidConfiguration("Static builds of Qt WebEngine are not supported")
if not (self.options.gui and self.options.qtdeclarative and self.options.qtlocation and self.options.qtwebchannel):
raise ConanInvalidConfiguration("option qt:qtwebengine requires also qt:gui, qt:qtdeclarative, qt:qtlocation and qt:qtwebchannel")
if hasattr(self, "settings_build") and cross_building(self, skip_x64_x86=True):
raise ConanInvalidConfiguration("Cross compiling Qt WebEngine is not supported")
if self.settings.compiler == "gcc" and Version(self.settings.compiler.version) < "5":
raise ConanInvalidConfiguration("Compiling Qt WebEngine with gcc < 5 is not supported")
if self.settings.os == "Android":
if self.options.get_safe("opengl", "no") == "desktop":
raise ConanInvalidConfiguration("OpenGL desktop is not supported on Android. Consider using OpenGL es2")
if not self.options.get_safe("android_sdk", ""):
raise ConanInvalidConfiguration("Path to Android SDK is required to build Qt")
if self.settings.os != "Windows" and self.options.get_safe("opengl", "no") == "dynamic":
raise ConanInvalidConfiguration("Dynamic OpenGL is supported only on Windows.")
if self.options.get_safe("with_fontconfig", False) and not self.options.get_safe("with_freetype", False):
raise ConanInvalidConfiguration("with_fontconfig cannot be enabled if with_freetype is disabled.")
if not self.options.with_doubleconversion and str(self.settings.compiler.libcxx) != "libc++":
raise ConanInvalidConfiguration("Qt without libc++ needs qt:with_doubleconversion. "
"Either enable qt:with_doubleconversion or switch to libc++")
if is_msvc_static_runtime(self) and self.options.shared:
raise ConanInvalidConfiguration("Qt cannot be built as shared library with static runtime")
if self.settings.compiler == "apple-clang":
if Version(self.settings.compiler.version) < "10.0":
raise ConanInvalidConfiguration("Old versions of apple sdk are not supported by Qt (QTBUG-76777)")
if self.settings.compiler in ["gcc", "clang"]:
if Version(self.settings.compiler.version) < "5.0":
raise ConanInvalidConfiguration("qt 5.15.X does not support GCC or clang before 5.0")
if self.options.get_safe("with_pulseaudio", default=False) and not self.dependencies["pulseaudio"].options.with_glib:
# https://bugreports.qt.io/browse/QTBUG-95952
raise ConanInvalidConfiguration("Pulseaudio needs to be built with glib option or qt's configure script won't detect it")
if self.settings.os in ['Linux', 'FreeBSD']:
if self.options.with_gssapi:
raise ConanInvalidConfiguration("gssapi cannot be enabled until conan-io/conan-center-index#4102 is closed")
if self.options.get_safe("with_x11", False) and not self.dependencies.direct_host["xkbcommon"].options.with_x11:
raise ConanInvalidConfiguration("The 'with_x11' option for the 'xkbcommon' package must be enabled when the 'with_x11' option is enabled")
if self.options.get_safe("qtwayland", False) and not self.dependencies.direct_host["xkbcommon"].options.with_wayland:
raise ConanInvalidConfiguration("The 'with_wayland' option for the 'xkbcommon' package must be enabled when the 'qtwayland' option is enabled")
if cross_building(self) and self.options.cross_compile == "None" and not is_apple_os(self) and self.settings.os != "Android":
raise ConanInvalidConfiguration("option cross_compile must be set for cross compilation "
"cf https://doc.qt.io/qt-5/configure-options.html#cross-compilation-options")
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")
def requirements(self):
self.requires("zlib/[>=1.2.11 <2]")
if self.options.openssl:
self.requires("openssl/[>=1.1 <4]")
if self.options.with_pcre2:
self.requires("pcre2/[>=10.42]")
if self.options.get_safe("with_vulkan"):
self.requires("vulkan-loader/1.3.268.0")
if is_apple_os(self):
self.requires("moltenvk/1.2.2")
if self.options.with_glib:
self.requires("glib/2.78.3")
# if self.options.with_libiconv: # QTBUG-84708
# self.requires("libiconv/1.16")# QTBUG-84708
if self.options.with_doubleconversion and not self.options.multiconfiguration:
self.requires("double-conversion/3.3.0")
if self.options.get_safe("with_freetype", False) and not self.options.multiconfiguration:
self.requires("freetype/2.13.2")
if self.options.get_safe("with_fontconfig", False):
self.requires("fontconfig/2.15.0")
if self.options.get_safe("with_icu", False):
self.requires("icu/74.2")
if self.options.get_safe("with_harfbuzz", False) and not self.options.multiconfiguration:
self.requires("harfbuzz/8.3.0")
if self.options.get_safe("with_libjpeg", False) and not self.options.multiconfiguration:
if self.options.with_libjpeg == "libjpeg-turbo":
self.requires("libjpeg-turbo/[>=3.0 <3.1]")
else:
self.requires("libjpeg/9e")
if self.options.get_safe("with_libpng", False) and not self.options.multiconfiguration:
self.requires("libpng/[>=1.6 <2]")
if self.options.with_sqlite3 and not self.options.multiconfiguration:
self.requires("sqlite3/[>=3.45.0 <4]")
if self.options.get_safe("with_mysql", False):
self.requires("libmysqlclient/8.1.0")
if self.options.with_pq:
self.requires("libpq/15.4")
if self.options.with_odbc:
if self.settings.os != "Windows":
self.requires("odbc/2.3.11")
if self.options.get_safe("with_openal", False):
self.requires("openal-soft/1.22.2")
if self.options.get_safe("with_libalsa", False):
self.requires("libalsa/1.2.10")
if self.options.get_safe("with_x11"):
self.requires("xorg/system")
if self.options.get_safe("with_x11") or self.options.qtwayland:
self.requires("xkbcommon/1.5.0")
if self.options.get_safe("opengl", "no") != "no":
self.requires("opengl/system")
if self.options.with_zstd:
self.requires("zstd/[>=1.5.5]")
if self.options.qtwebengine and self.settings.os in ["Linux", "FreeBSD"]:
self.requires("expat/[>=2.6.2 <3]")
self.requires("opus/1.4")
if not self.options.qtwayland:
self.requires("xorg-proto/2022.2")
self.requires("libxshmfence/1.3")
self.requires("nss/3.93")
self.requires("libdrm/2.4.119")
self.requires("egl/system")
if self.options.get_safe("with_gstreamer", False):
self.requires("gst-plugins-base/1.19.2")
if self.options.get_safe("with_pulseaudio", False):
self.requires("pulseaudio/14.2")
if self.options.with_dbus:
self.requires("dbus/1.15.8")
if self.options.qtwayland:
self.requires("wayland/1.22.0")
if self.settings.os in ['Linux', 'FreeBSD'] and self.options.with_gssapi:
self.requires("krb5/1.18.3") # conan-io/conan-center-index#4102
if self.options.get_safe("with_atspi"):
self.requires("at-spi2-core/2.51.0")
if self.options.get_safe("with_md4c", False):
self.requires("md4c/0.4.8")
def package_id(self):
del self.info.options.cross_compile
del self.info.options.sysroot
if self.info.options.multiconfiguration:
if self.info.settings.compiler == "Visual Studio":
if "MD" in self.info.settings.compiler.runtime:
self.info.settings.compiler.runtime = "MD/MDd"
else:
self.info.settings.compiler.runtime = "MT/MTd"
elif self.info.settings.compiler == "msvc":
self.info.settings.compiler.runtime_type = "Release/Debug"
if self.info.settings.os == "Android":
del self.info.options.android_sdk
def build_requirements(self):
if self._settings_build.os == "Windows" and is_msvc(self):
self.tool_requires("jom/[>=1.1 <2]")
if self.options.qtwebengine:
self.tool_requires("ninja/[>=1.12 <2]")
self.tool_requires("nodejs/18.15.0")
self.tool_requires("gperf/3.1")
# gperf, bison, flex, python >= 2.7.5 & < 3
if self._settings_build.os == "Windows":
self.tool_requires("winflexbison/2.5.25")
else:
self.tool_requires("bison/3.8.2")
self.tool_requires("flex/2.6.4")
if self.options.qtwayland:
self.tool_requires("wayland/<host_version>")
@property
def angle_path(self):
return os.path.join(self.source_folder, "angle")
def source(self):
get(self, **self.conan_data["sources"][self.version],
strip_root=True, destination="qt5")
apply_conandata_patches(self)
for f in ["renderer", os.path.join("renderer", "core"), os.path.join("renderer", "platform")]:
replace_in_file(self, os.path.join(self.source_folder, "qt5", "qtwebengine", "src", "3rdparty", "chromium", "third_party", "blink", f, "BUILD.gn"),
" if (enable_precompiled_headers) {\n if (is_win) {",
" if (enable_precompiled_headers) {\n if (false) {"
)
replace_in_file(self, os.path.join(self.source_folder, "qt5", "qtbase", "configure.json"),
"-ldbus-1d",
"-ldbus-1"
)
save(self, os.path.join(self.source_folder, "qt5", "qtbase", "mkspecs", "features", "uikit", "bitcode.prf"), "")
# shorten the path to ANGLE to avoid the following error:
# C:\J2\w\prod-v2\bsr@4\104220\ebfcf\p\qtde01f793a6074\s\qt5\qtbase\src\3rdparty\angle\src\libANGLE\renderer\d3d\d3d11\texture_format_table_autogen.cpp : fatal error C1083: Cannot open compiler generated file: '': Invalid argument
copy(self, "*", os.path.join(self.source_folder, "qt5", "qtbase", "src", "3rdparty", "angle"), self.angle_path)
def generate(self):
pc = PkgConfigDeps(self)
pc.generate()
ms = VCVars(self)
ms.generate()
vbe = VirtualBuildEnv(self)
vbe.generate()
if not cross_building(self):
vre = VirtualRunEnv(self)
vre.generate(scope="build")
env = Environment()
env.define("MAKEFLAGS", f"j{build_jobs(self)}")
env.define("ANGLE_DIR", self.angle_path)
env.prepend_path("PKG_CONFIG_PATH", self.generators_folder)
if self.settings.os == "Windows":
env.prepend_path("PATH", os.path.join(self.source_folder, "qt5", "gnuwin32", "bin"))
env.vars(self).save_script("conan_qt_env_file")
def _make_program(self):
if is_msvc(self):
return "jom"
elif self._settings_build.os == "Windows":
return "mingw32-make"
else:
return "make"
def _xplatform(self):
if self.settings.os == "Linux":
if self.settings.compiler == "gcc":
return {"x86": "linux-g++-32",
"armv6": "linux-arm-gnueabi-g++",
"armv7": "linux-arm-gnueabi-g++",
"armv7hf": "linux-arm-gnueabi-g++",
"armv8": "linux-aarch64-gnu-g++"}.get(str(self.settings.arch), "linux-g++")
elif self.settings.compiler == "clang":
if self.settings.arch == "x86":
return "linux-clang-libc++-32" if self.settings.compiler.libcxx == "libc++" else "linux-clang-32"
elif self.settings.arch == "x86_64":
return "linux-clang-libc++" if self.settings.compiler.libcxx == "libc++" else "linux-clang"
elif self.settings.os == "Macos":
return {"clang": "macx-clang",
"apple-clang": "macx-clang",
"gcc": "macx-g++"}.get(str(self.settings.compiler))
elif self.settings.os == "iOS":
if self.settings.compiler == "apple-clang":
return "macx-ios-clang"
elif self.settings.os == "watchOS":
if self.settings.compiler == "apple-clang":
return "macx-watchos-clang"
elif self.settings.os == "tvOS":
if self.settings.compiler == "apple-clang":
return "macx-tvos-clang"
elif self.settings.os == "Android":
if self.settings.compiler == "clang":
return "android-clang"
elif self.settings.os == "Windows":
return {
"Visual Studio": "win32-msvc",
"msvc": "win32-msvc",
"gcc": "win32-g++",
"clang": "win32-clang-g++",
}.get(str(self.settings.compiler))
elif self.settings.os == "WindowsStore":
if is_msvc(self):
if str(self.settings.compiler) == "Visual Studio":
msvc_version = str(self.settings.compiler.version)
else:
msvc_version = {
"190": "14",
"191": "15",
"192": "16",
}.get(str(self.settings.compiler.version))
return {
"14": {
"armv7": "winrt-arm-msvc2015",
"x86": "winrt-x86-msvc2015",
"x86_64": "winrt-x64-msvc2015",
},
"15": {
"armv7": "winrt-arm-msvc2017",
"x86": "winrt-x86-msvc2017",
"x86_64": "winrt-x64-msvc2017",
},
"16": {
"armv7": "winrt-arm-msvc2019",
"x86": "winrt-x86-msvc2019",
"x86_64": "winrt-x64-msvc2019",
}
}.get(msvc_version).get(str(self.settings.arch))
elif self.settings.os == "FreeBSD":
return {"clang": "freebsd-clang",
"gcc": "freebsd-g++"}.get(str(self.settings.compiler))
elif self.settings.os == "SunOS":
if self.settings.compiler == "sun-cc":
if self.settings.arch == "sparc":
return "solaris-cc-stlport" if self.settings.compiler.libcxx == "libstlport" else "solaris-cc"
elif self.settings.arch == "sparcv9":
return "solaris-cc64-stlport" if self.settings.compiler.libcxx == "libstlport" else "solaris-cc64"
elif self.settings.compiler == "gcc":
return {"sparc": "solaris-g++",
"sparcv9": "solaris-g++-64"}.get(str(self.settings.arch))
elif self.settings.os == "Neutrino" and self.settings.compiler == "qcc":
return {"armv8": "qnx-aarch64le-qcc",
"armv8.3": "qnx-aarch64le-qcc",
"armv7": "qnx-armle-v7-qcc",
"armv7hf": "qnx-armle-v7-qcc",
"armv7s": "qnx-armle-v7-qcc",
"armv7k": "qnx-armle-v7-qcc",
"x86": "qnx-x86-qcc",
"x86_64": "qnx-x86-64-qcc"}.get(str(self.settings.arch))
elif self.settings.os == "Emscripten" and self.settings.arch == "wasm":
return "wasm-emscripten"
return None
def build(self):
args = ["-confirm-license", "-silent", "-nomake examples", "-nomake tests",
f"-prefix {self.package_folder}"]
if cross_building(self):
args.append(f"-extprefix {self.package_folder}")
args.append("-v")
if self.options.commercial:
args.append("-commercial")
else:
args.append("-opensource")
if not self.options.gui:
args.append("-no-gui")
if not self.options.widgets:
args.append("-no-widgets")
if not self.options.shared:
args.insert(0, "-static")
if is_msvc(self) and "MT" in msvc_runtime_flag(self):
args.append("-static-runtime")
else:
args.insert(0, "-shared")
if self.options.multiconfiguration:
args.append("-debug-and-release")
elif self.settings.build_type == "Debug":
args.append("-debug")
elif self.settings.build_type == "Release":
args.append("-release")
elif self.settings.build_type == "RelWithDebInfo":
args.append("-release")
args.append("-force-debug-info")
elif self.settings.build_type == "MinSizeRel":
args.append("-release")
args.append("-optimize-size")
for module in self._submodules:
if module in self.options and not self.options.get_safe(module):
args.append("-skip " + module)
args.append("--zlib=system")
# openGL
opengl = self.options.get_safe("opengl", "no")
if opengl == "no":
args += ["-no-opengl"]
elif opengl == "es2":
args += ["-opengl es2"]
elif opengl == "desktop":
args += ["-opengl desktop"]
elif opengl == "dynamic":
args += ["-opengl dynamic"]
if self.options.get_safe("with_vulkan", False):
args.append("-vulkan")
else:
args.append("-no-vulkan")
# openSSL
if not self.options.openssl:
args += ["-no-openssl"]
else:
if self.dependencies["openssl"].options.shared:
args += ["-openssl-runtime"]
else:
args += ["-openssl-linked"]
# args.append("--iconv=" + ("gnu" if self.options.with_libiconv else "no"))# QTBUG-84708
args.append("--glib=" + ("yes" if self.options.with_glib else "no"))
args.append("--pcre=" + ("system" if self.options.with_pcre2 else "qt"))
args.append("--fontconfig=" + ("yes" if self.options.get_safe("with_fontconfig", False) else "no"))
args.append("--icu=" + ("yes" if self.options.get_safe("with_icu", False) else "no"))
args.append("--sql-mysql=" + ("yes" if self.options.get_safe("with_mysql", False) else "no"))
args.append("--sql-psql=" + ("yes" if self.options.with_pq else "no"))
args.append("--sql-odbc=" + ("yes" if self.options.with_odbc else "no"))
args.append("--zstd=" + ("yes" if self.options.with_zstd else "no"))
if self.options.qtmultimedia:
args.append("--alsa=" + ("yes" if self.options.get_safe("with_libalsa", False) else "no"))
args.append("--gstreamer" if self.options.get_safe("with_gstreamer", False) else "--no-gstreamer")
args.append("--pulseaudio" if self.options.get_safe("with_pulseaudio", False) else "--no-pulseaudio")
if self.options.with_dbus:
args.append("-dbus-linked")
else:
args.append("-no-dbus")
args.append("-feature-gssapi" if self.options.get_safe("with_gssapi", False) else "-no-feature-gssapi")
for opt, conf_arg in [
("with_doubleconversion", "doubleconversion"),
("with_freetype", "freetype"),
("with_harfbuzz", "harfbuzz"),
("with_libjpeg", "libjpeg"),
("with_libpng", "libpng"),
("with_sqlite3", "sqlite"),
("with_md4c", "libmd4c")]:
if self.options.get_safe(opt, False):
if self.options.multiconfiguration:
args += ["-qt-" + conf_arg]
else:
args += ["-system-" + conf_arg]
else:
args += ["-no-" + conf_arg]
libmap = [("zlib", "ZLIB"),
("openssl", "OPENSSL"),
("pcre2", "PCRE2"),
("glib", "GLIB"),
# ("libiconv", "ICONV"),# QTBUG-84708
("double-conversion", "DOUBLECONVERSION"),
("freetype", "FREETYPE"),
("fontconfig", "FONTCONFIG"),
("icu", "ICU"),
("harfbuzz", "HARFBUZZ"),
("libjpeg", "LIBJPEG"),
("libjpeg-turbo", "LIBJPEG"),
("libpng", "LIBPNG"),
("sqlite3", "SQLITE"),
("libmysqlclient", "MYSQL"),
("libpq", "PSQL"),
("odbc", "ODBC"),
("sdl2", "SDL2"),
("openal-soft", "OPENAL"),
("zstd", "ZSTD"),
("libalsa", "ALSA"),
("xkbcommon", "XKBCOMMON"),
("md4c", "LIBMD4C")]
for package, var in libmap:
if package in [d.ref.name for d in self.dependencies.direct_host.values()]:
p = self.dependencies[package]
if package == "freetype":
args.append("\"%s_INCDIR=%s\"" % (var, p.cpp_info.aggregated_components().includedirs[-1]))
args.append("\"%s_LIBS=%s\"" % (var, " ".join(self._gather_libs(p))))
for dependency in self.dependencies.direct_host.values():
args += [f"-I \"{s}\"" for s in dependency.cpp_info.aggregated_components().includedirs]
args += [f"-D {s}" for s in dependency.cpp_info.aggregated_components().defines]
libdirs = [l for dependency in self.dependencies.host.values() for l in dependency.cpp_info.aggregated_components().libdirs]
args.append("QMAKE_LIBDIR+=\"%s\"" % " ".join(libdirs))
if not is_msvc(self):
args.append("QMAKE_RPATHLINKDIR+=\"%s\"" % ":".join(libdirs))
if "libmysqlclient" in [d.ref.name for d in self.dependencies.direct_host.values()]:
args.append("-mysql_config \"%s\"" % os.path.join(self.dependencies["libmysqlclient"].package_folder, "bin", "mysql_config"))
if "libpq" in [d.ref.name for d in self.dependencies.direct_host.values()]:
args.append("-psql_config \"%s\"" % os.path.join(self.dependencies["libpq"].package_folder, "bin", "pg_config"))
if self.settings.os == "Macos":
args += ["-no-framework"]
if self.settings.arch == "armv8":
args.append('QMAKE_APPLE_DEVICE_ARCHS="arm64"')
elif self.settings.os == "Android":
args += [f"-android-ndk-platform android-{self.settings.os.api_level}"]
args += [f"-android-abis {android_abi(self)}"]
if self.settings.get_safe("compiler.libcxx") == "libstdc++":
args += ["-D_GLIBCXX_USE_CXX11_ABI=0"]
elif self.settings.get_safe("compiler.libcxx") == "libstdc++11":
args += ["-D_GLIBCXX_USE_CXX11_ABI=1"]
if self.options.get_safe("android_sdk", ""):
args += [f"-android-sdk {self.options.android_sdk}"]
if self.options.sysroot:
args += [f"-sysroot {self.options.sysroot}"]
if self.options.device:
args += [f"-device {self.options.device}"]
else:
xplatform_val = self._xplatform()
if xplatform_val:
if not cross_building(self, skip_x64_x86=True):
args += [f"-platform {xplatform_val}"]
else:
args += [f"-xplatform {xplatform_val}"]
else:
self.output.warn("host not supported: %s %s %s %s" %
(self.settings.os, self.settings.compiler,
self.settings.compiler.version, self.settings.arch))
if self.options.cross_compile:
args += [f"-device-option CROSS_COMPILE={self.options.cross_compile}"]
def _getenvpath(var):
val = os.getenv(var)
if val and self._settings_build.os == "Windows":
val = val.replace("\\", "/")
os.environ[var] = val
return val
if not is_msvc(self):
value = _getenvpath("CC")
if value:
args += ['QMAKE_CC="' + value + '"',
'QMAKE_LINK_C="' + value + '"',
'QMAKE_LINK_C_SHLIB="' + value + '"']
value = _getenvpath('CXX')
if value:
args += ['QMAKE_CXX="' + value + '"',
'QMAKE_LINK="' + value + '"',
'QMAKE_LINK_SHLIB="' + value + '"']
if self._settings_build.os == "Linux" and self.settings.compiler == "clang":
args += ['QMAKE_CXXFLAGS+="-ftemplate-depth=1024"']
if self._settings_build.os == "Macos":
# On macOS, SIP resets DYLD_LIBRARY_PATH injected by VirtualBuildEnv & VirtualRunEnv.
# Qt builds several executables (moc etc) which are called later on during build of
# libraries, and these executables link to several external dependencies in requirements().
# If these external libs are shared, moc calls fail because its dylib dependencies
# are not found (unless they can be accidentally found in system paths).
# So the workaround is to add libdirs of these external dependencies to LC_RPATH
# of runtime artifacts.
if not cross_building(self):
for libpath in VirtualRunEnv(self).vars().get("DYLD_LIBRARY_PATH", "").split(":"):
# see https://doc.qt.io/qt-5/qmake-variable-reference.html#qmake-rpathdir
args += [f"QMAKE_RPATHDIR+=\"{libpath}\""]
if self.settings.compiler == "apple-clang" and self.options.qtmultimedia:
# XCode 14.3 finally removes std::unary_function, so compilation fails
# when using newer SDKs when using C++17 or higher.
# This macro re-enables them. Should be safe to pass this macro even
# in earlier versions, as it would have no effect.
args += ['QMAKE_CXXFLAGS+="-D_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION=1"']
if self.options.qtwebengine and self.settings.os in ["Linux", "FreeBSD"]:
args += ["-qt-webengine-ffmpeg",
"-system-webengine-opus",
"-webengine-jumbo-build 0"]
if self.options.config:
args.append(str(self.options.config))
os.mkdir("build_folder")
with chdir(self, "build_folder"):
if self._settings_build.os == "Macos":
save(self, ".qmake.stash" , "")
save(self, ".qmake.super" , "")
self.run("%s %s" % (os.path.join(self.source_folder, "qt5", "configure"), " ".join(args)))
self.run(self._make_program())
@property
def _cmake_core_extras_file(self):
return os.path.join("lib", "cmake", "Qt5Core", "conan_qt_core_extras.cmake")
def _cmake_qt5_private_file(self, module):
return os.path.join("lib", "cmake", f"Qt5{module}", f"conan_qt_qt5_{module.lower()}private.cmake")
def package(self):
with chdir(self, "build_folder"):
self.run(f"{self._make_program()} install")
save(self, os.path.join(self.package_folder, "bin", "qt.conf"), """[Paths]
Prefix = ..""")
copy(self, "*LICENSE*", os.path.join(self.source_folder, "qt5/"), os.path.join(self.package_folder, "licenses"),
excludes="qtbase/examples/*")
for module in self._submodules:
if 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"))
#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)
rm(self, "*.pdb*", os.path.join(self.package_folder, "lib"), recursive=True)
rm(self, "*.pdb", os.path.join(self.package_folder, "bin"), recursive=True)
rm(self, "*.pdb", os.path.join(self.package_folder, "plugins"), recursive=True)
# "Qt5Bootstrap" is internal Qt library - removing it to avoid linking error, since it contains
# symbols that are also in "Qt5Core.lib". It looks like there is no "Qt5Bootstrap.dll".
for fl in glob.glob(os.path.join(self.package_folder, "lib", "*Qt5Bootstrap*")):
os.remove(fl)
#for m in os.listdir(os.path.join(self.package_folder, "lib", "cmake")):
# module = os.path.join(self.package_folder, "lib", "cmake", m, f"{m}Macros.cmake")
# if not os.path.isfile(module):
# rmdir(self, os.path.join(self.package_folder, "lib", "cmake", m))
extension = ""
if self._settings_build.os == "Windows":
extension = ".exe"
def package_info(self):
self.cpp_info.set_property("cmake_file_name", "Qt5")
self.cpp_info.set_property("pkg_config_name", "qt5")
self.cpp_info.names["cmake_find_package"] = "Qt5"
self.cpp_info.names["cmake_find_package_multi"] = "Qt5"
self.cpp_info.builddirs = [os.path.join(self.package_folder, "lib", "cmake", "Qt5")]
self.cpp_info.requires = ["zlib::zlib"]
if self.options.with_pcre2:
self.cpp_info.requires.append("pcre2::pcre2")
if self.options.with_doubleconversion:
self.cpp_info.requires.append("double-conversion::double-conversion")
if self.options.get_safe("with_icu", False):
self.cpp_info.requires.append("icu::icu")
if self.options.with_zstd:
self.cpp_info.requires.append("zstd::zstd")
if self.options.with_glib:
self.cpp_info.requires.append("glib::glib-2.0")
if self.options.with_dbus:
self.cpp_info.requires.append("dbus::dbus")
if self.options.gui:
if self.options.with_freetype:
self.cpp_info.requires.append("freetype::freetype")
if self.options.with_libpng:
self.cpp_info.requires.append("libpng::libpng")
if self.options.get_safe("with_fontconfig", False):
self.cpp_info.requires.append("fontconfig::fontconfig")
if self.settings.os in ["Linux", "FreeBSD"]:
if self.options.qtwayland or self.options.get_safe("with_x11", False):
self.cpp_info.requires.append("xkbcommon::xkbcommon")
if self.options.get_safe("with_x11", False):
self.cpp_info.requires.append("xorg::xorg")
if self.options.get_safe("opengl", "no") != "no":
self.cpp_info.requires.append("opengl::opengl")
if self.options.get_safe("with_vulkan", False):
self.cpp_info.requires.append("vulkan-loader::vulkan-loader")
if is_apple_os(self):
self.cpp_info.requires.append("moltenvk::moltenvk")
if self.options.with_harfbuzz:
self.cpp_info.requires.append("harfbuzz::harfbuzz")
if self.options.with_libjpeg == "libjpeg-turbo":
self.cpp_info.requires.append("libjpeg-turbo::libjpeg-turbo")
if self.options.with_libjpeg == "libjpeg":
self.cpp_info.requires.append("libjpeg::libjpeg")
if self.options.with_md4c:
self.cpp_info.requires.append("md4c::md4c")
if self.options.with_glib:
self.cpp_info.requires.append("glib::glib")
if self.options.get_safe("with_fontconfig"):
self.cpp_info.requires.append("fontconfig::fontconfig")
if self.options.get_safe("with_freetype"):
self.cpp_info.requires.append("freetype::freetype")
if self.settings.os in ["Linux", "FreeBSD"]:
self.cpp_info.requires.append("xkbcommon::libxkbcommon-x11")
if self.options.get_safe("with_x11", False):
self.cpp_info.requires.append("xorg::xorg")
if self.options.with_dbus and self.options.with_atspi:
self.cpp_info.requires.append("at-spi2-core::at-spi2-core")
if self.options.with_sqlite3:
self.cpp_info.requires.append("sqlite3::sqlite3")
if self.options.with_pq:
self.cpp_info.requires.append("libpq::libpq")
if self.options.get_safe("with_mysql", False):
self.cpp_info.requires.append("libmysqlclient::libmysqlclient")
if self.options.with_odbc:
if self.settings.os != "Windows":
self.cpp_info.requires.append("odbc::odbc")
if self.options.openssl:
self.cpp_info.requires.append("openssl::openssl")
if self.settings.os in ['Linux', 'FreeBSD'] and self.options.with_gssapi:
self.cpp_info.requires.append("krb5::krb5-gssapi")
if self.options.qtmultimedia:
if self.options.get_safe("with_libalsa", False):
self.cpp_info.requires.append("libalsa::libalsa")
if self.options.with_openal:
self.cpp_info.requires.append("openal-soft::openal-soft")
if self.options.get_safe("with_pulseaudio", False):
self.cpp_info.requires.append("pulseaudio::pulse")
self.cpp_info.requires.append("libalsa::libalsa")
if self.options.qtwebengine:
if self.settings.os in ["Linux", "FreeBSD"]:
self.cpp_info.extend(["expat::expat", "opus::libopus", "xorg-proto::xorg-proto", "libxshmfence::libxshmfence", \
"nss::nss", "libdrm::libdrm", "egl::egl"])
if self.options.qtwayland and self.options.gui:
self.cpp_info.requires.extend(["wayland::wayland-client", "wayland:wayland-server"])
@staticmethod
def _remove_duplicate(l):
seen = set()
seen_add = seen.add
for element in itertools.filterfalse(seen.__contains__, l):
seen_add(element)
yield element
def _gather_libs(self, p):
libs = ["-l" + i for i in p.cpp_info.aggregated_components().libs + p.cpp_info.aggregated_components().system_libs]
if is_apple_os(self):
libs += ["-framework " + i for i in p.cpp_info.aggregated_components().frameworks]
libs += p.cpp_info.aggregated_components().sharedlinkflags
for dep in p.dependencies.direct_host.values():
libs += self._gather_libs(dep)
return self._remove_duplicate(libs)