diff options
| -rw-r--r-- | README.pyside6.md | 7 | ||||
| -rw-r--r-- | README.pyside6_addons.md | 62 | ||||
| -rw-r--r-- | README.pyside6_essentials.md | 56 | ||||
| -rw-r--r-- | build_scripts/main.py | 18 | ||||
| -rw-r--r-- | build_scripts/wheel_files.py | 893 | ||||
| -rw-r--r-- | coin_build_instructions.py | 10 | ||||
| -rw-r--r-- | coin_test_instructions.py | 11 | ||||
| -rw-r--r-- | create_wheels.py | 417 | ||||
| -rw-r--r-- | requirements.txt | 2 | ||||
| -rw-r--r-- | testing/wheel_tester.py | 19 | ||||
| -rw-r--r-- | wheel_artifacts/pyproject.toml | 3 | ||||
| -rw-r--r-- | wheel_artifacts/setup.cfg.base | 52 | ||||
| -rw-r--r-- | wheel_artifacts/setup.py.base | 29 |
13 files changed, 1568 insertions, 11 deletions
diff --git a/README.pyside6.md b/README.pyside6.md index 007903291..0ece3ba72 100644 --- a/README.pyside6.md +++ b/README.pyside6.md @@ -23,10 +23,15 @@ and [PyPi](https://pypi.org/project/PySide6/): pip install PySide6 ``` +> Please note: this wheel is an alias to other two wheels +> [PySide6_Essentials](https://pypi.org/project/PySide6_Essentials) and +> [PySide6_Addons](https://pypi.org/project/PySide6_Addons), which contains +> a predefined list of Qt Modules. + #### Dependencies PySide6 versions following 6.0 use a C++ parser based on -[Clang](http://clang.org/). The Clang library (C-bindings), version 10.0 or +[Clang](http://clang.org/). The Clang library (C-bindings), version 13.0 or higher is required for building. Prebuilt versions of it can be downloaded from [download.qt.io](http://download.qt.io/development_releases/prebuilt/libclang/). diff --git a/README.pyside6_addons.md b/README.pyside6_addons.md new file mode 100644 index 000000000..f6e2e6605 --- /dev/null +++ b/README.pyside6_addons.md @@ -0,0 +1,62 @@ +# PySide6 Addons + +PySide6 is the official Python module from the +[Qt for Python project](http://wiki.qt.io/Qt_for_Python), +which provides access to the complete Qt 6.0+ framework. + +The Qt for Python project is developed in the open, with all facilities you'd expect +from any modern OSS project such as all code in a git repository and an open +design process. We welcome any contribution conforming to the +[Qt Contribution Agreement](https://www.qt.io/contributionagreement/). + +This is a complementary wheel for [PySide6](https://pypi.org/project/PySide6), +it includes the following Qt modules: + +* Qt3DAnimation +* Qt3DCore +* Qt3DExtras +* Qt3DInput +* Qt3DLogic +* Qt3DRender +* QtAxContainer +* QtBluetooth +* QtCharts +* QtDataVisualization +* QtMultimedia +* QtMultimediaWidgets +* QtNetworkAuth +* QtNfc +* QtPositioning +* QtQuick3D +* QtRemoteObjects +* QtScxml +* QtSensors +* QtSerialPort +* QtStateMachine +* QtVirtualKeyboard +* QtWebChannel +* QtWebEngineCore +* QtWebEngineQuick +* QtWebEngineWidgets +* QtWebSockets + +### Documentation and Bugs + +You can find more information about the PySide6 module API in the +[official Qt for Python documentation](https://doc.qt.io/qtforpython/). + +If you come across any issue, please file a bug report at our +[JIRA tracker](https://bugreports.qt.io/projects/PYSIDE) following +our [guidelines](https://wiki.qt.io/Qt_for_Python/Reporting_Bugs). + +### Community + +Check *#qt-pyside*, our official IRC channel on FreeNode, or contact us via our +[mailing list](http://lists.qt-project.org/mailman/listinfo/pyside). + +### Licensing + +PySide6 is available under both Open Source (LGPLv3/GPLv2) and commercial +license. Using PyPi is the recommended installation source, because the +content of the wheels is valid for both cases. For more information, refer to +the [Qt Licensing page](https://www.qt.io/licensing/). diff --git a/README.pyside6_essentials.md b/README.pyside6_essentials.md new file mode 100644 index 000000000..629db92ef --- /dev/null +++ b/README.pyside6_essentials.md @@ -0,0 +1,56 @@ +# PySide6 Essentials + +PySide6 is the official Python module from the +[Qt for Python project](http://wiki.qt.io/Qt_for_Python), +which provides access to the complete Qt 6.0+ framework. + +The Qt for Python project is developed in the open, with all facilities you'd expect +from any modern OSS project such as all code in a git repository and an open +design process. We welcome any contribution conforming to the +[Qt Contribution Agreement](https://www.qt.io/contributionagreement/). + +This is a minimal wheel for [PySide6](https://pypi.org/project/PySide6), +it includes only the essentials Qt modules: + +* QtCore +* QtGui +* QtWidgets +* QtHelp +* QtNetwork +* QtConcurrent +* QtDBus +* QtDesigner +* QtOpenGL +* QtOpenGLWidgets +* QtPrintSupport +* QtQml +* QtQuick +* QtQuickControls2 +* QtQuickWidgets +* QtXml +* QtTest +* QtSql +* QtSvg +* QtSvgWidgets +* QtUiTools + +### Documentation and Bugs + +You can find more information about the PySide6 module API in the +[official Qt for Python documentation](https://doc.qt.io/qtforpython/). + +If you come across any issue, please file a bug report at our +[JIRA tracker](https://bugreports.qt.io/projects/PYSIDE) following +our [guidelines](https://wiki.qt.io/Qt_for_Python/Reporting_Bugs). + +### Community + +Check *#qt-pyside*, our official IRC channel on FreeNode, or contact us via our +[mailing list](http://lists.qt-project.org/mailman/listinfo/pyside). + +### Licensing + +PySide6 is available under both Open Source (LGPLv3/GPLv2) and commercial +license. Using PyPi is the recommended installation source, because the +content of the wheels is valid for both cases. For more information, refer to +the [Qt Licensing page](https://www.qt.io/licensing/). diff --git a/build_scripts/main.py b/build_scripts/main.py index e50ca791e..75850691b 100644 --- a/build_scripts/main.py +++ b/build_scripts/main.py @@ -44,6 +44,7 @@ import platform import re import sys import importlib +from pathlib import Path from textwrap import dedent import time from .config import config @@ -443,6 +444,23 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin): # Build packages _build.run(self) + + # Keep packaged directories for wheel construction + # This is to take advantage of the packaging step + # to keep the data in the proper structure to create + # a wheel. + _path = Path(self.st_build_dir) + _wheel_path = _path.parent / "package_for_wheels" + if not _wheel_path.exists(): + _wheel_path.mkdir() + _package_name = os.listdir(_path)[0] + _src = Path(_path / _package_name) + _dst = Path(_wheel_path / _package_name) + try: + Path(_path / _package_name).rename(_wheel_path / _package_name) + except Exception as e: + log.warn(f'***** problem renaming "{self.st_build_dir}"') + log.warn(f'ignored error: {type(e).__name__}: {e}') else: log.info("Skipped preparing and building packages.") log.info(f"--- Build completed ({elapsed()}s)") diff --git a/build_scripts/wheel_files.py b/build_scripts/wheel_files.py new file mode 100644 index 000000000..237a16b38 --- /dev/null +++ b/build_scripts/wheel_files.py @@ -0,0 +1,893 @@ +############################################################################# +## +## Copyright (C) 2022 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + + +import sys +from dataclasses import Field, dataclass, field +from typing import Any, Dict, List + + +# This dataclass is in charge of holding the file information +# that each Qt module needs to have to be packaged in a wheel +@dataclass +class ModuleData: + name: str + ext: str = "" + # Libraries not related to Qt modules + lib: List[str] = field(default_factory=list) + # Libraries related to Qt modules + qtlib: List[str] = field(default_factory=list) + # Files from the Qt/qml directory + qml: List[str] = field(default_factory=list) + pyi: List[str] = field(default_factory=list) + translations: List[str] = field(default_factory=list) + typesystems: List[str] = field(default_factory=list) + include: List[str] = field(default_factory=list) + glue: List[str] = field(default_factory=list) + metatypes: List[str] = field(default_factory=list) + examples: List[str] = field(default_factory=list) + plugins: List[str] = field(default_factory=list) + + # For special cases when a file/directory doesn't fall into + # the previous categories. + extra_dirs: List[str] = field(default_factory=list) + extra_files: List[str] = field(default_factory=list) + + # Once the object is created, this method will be executed + # and automatically will initialize some of the files that are + # common for each module. + # Note: The goal of this list is to be used for a MANIFEST.in + # meaning that in case a file gets added and it doesn't + # exist, the wheel creation process will only throw a + # warning, but it will not interrupt the packaging process. + def __post_init__(self) -> None: + if not self.ext: + self.ext = self.get_extension_from_platform(sys.platform) + _lo = self.name.lower() + + self.lib.append(f"Qt{self.name}") + self.qtlib.append(f"libQt6{self.name}") + if not len(self.qml): + self.qml.append(f"Qt{self.name}") + self.pyi.append(f"Qt{self.name}.pyi") + self.typesystems.append(f"typesystem_{_lo}.xml") + self.include.append(f"Qt{self.name}/*.h") + self.glue.append(f"qt{_lo}.cpp") + if not len(self.metatypes): + self.metatypes.append(f"qt6{_lo}_relwithdebinfo_metatypes.json") + self.examples.append(f"{_lo}") + + # The PySide6 directory that gets packaged by the build_scripts + # 'prepare_packages()' has a certain structure that depends on + # the platform. Because that directory is the base for the wheel + # packaging to work, we use the relative paths that are included + # on each file. + # Note: The MANIFEST.in file doesn't need to have '\' or other + # separator, and respect the '/' even on Windows. + def adjusts_paths_and_extensions(self) -> None: + if sys.platform == "win32": + self.lib = [f"{i}.*{self.ext}".replace("lib", "") for i in self.lib] + self.qtlib = [f"{i}.*dll".replace("lib", "") for i in self.qtlib] + self.qml = [f"qml/{i}" for i in self.qml] + self.translations = [f"translations/{i}" for i in self.translations] + self.metatypes = [ + f"lib/metatypes/{i}".replace("_relwithdebinfo", "") for i in self.metatypes + ] + self.plugins = [f"plugins/{i}" for i in self.plugins] + else: + if sys.platform == "darwin": + self.qtlib = [f"Qt/lib/{i.replace('libQt6', 'Qt')}.framework" for i in self.qtlib] + self.lib = [self.macos_pyside_wrappers_lib(i) for i in self.lib] + else: + self.lib = [f"{i}.*{self.ext}*" for i in self.lib] + self.qtlib = [f"Qt/lib/{i}.*{self.ext}*" for i in self.qtlib] + self.qml = [f"Qt/qml/{i}" for i in self.qml] + self.translations = [f"Qt/translations/{i}" for i in self.translations] + self.metatypes = [f"Qt/lib/metatypes/{i}" for i in self.metatypes] + self.plugins = [f"Qt/plugins/{i}" for i in self.plugins] + + self.typesystems = [f"typesystems/{i}" for i in self.typesystems] + self.include = [f"include/{i}" for i in self.include] + self.glue = [f"glue/{i}" for i in self.glue] + self.examples = [f"examples/{i}" for i in self.examples] + + def macos_pyside_wrappers_lib(self, s): + if s.startswith("Qt"): + return f"{s}.*so*" + else: + return f"{s}.*{self.ext}*" + + @classmethod + def get_fields(cls) -> Dict[str, Field]: + return cls.__dataclass_fields__ + + @staticmethod + def get_extension_from_platform(platform: str) -> str: + if platform == "linux": + return "so" + elif platform == "darwin": + return "dylib" + elif platform == "win32": + return "pyd" + else: + print(f"Platform '{platform}' not supported. Exiting") + sys.exit(-1) + + +# Wheels auxiliary functions to return the ModuleData objects +# for each module that will be included in the wheel. + +# PySide wheel +def wheel_files_pyside_essentials() -> List[ModuleData]: + files = [ + module_QtCore(), + module_QtGui(), + module_QtWidgets(), + module_QtHelp(), + module_QtNetwork(), + module_QtConcurent(), + module_QtDBus(), + module_QtDesigner(), + module_QtOpenGL(), + module_QtOpenGLWidgets(), + module_QtPrintSupport(), + module_QtQml(), + module_QtQuick(), + module_QtQuickControls2(), + module_QtQuickWidgets(), + module_QtXml(), + module_QtTest(), + module_QtSql(), + module_QtSvg(), + module_QtSvgWidgets(), + module_QtUiTools(), + # Only for plugins + module_QtWayland(), + ] + return files + + +# PySide Addons wheel +def wheel_files_pyside_addons() -> List[ModuleData]: + files = [ + module_Qt3DAnimation(), + module_Qt3DCore(), + module_Qt3DExtras(), + module_Qt3DInput(), + module_Qt3DLogic(), + module_Qt3DRender(), + module_QtAxContainer(), + module_QtBluetooth(), + module_QtCharts(), + module_QtDataVisualization(), + module_QtMultimedia(), + module_QtMultimediaWidgets(), + module_QtNetworkAuth(), + module_QtNfc(), + module_QtPositioning(), + module_QtQuick3D(), + module_QtRemoteObjects(), + module_QtScxml(), + module_QtSensors(), + module_QtSerialPort(), + module_QtStateMachine(), + # Not available in 6.3 + #module_QtTextToSpeech(), + module_QtVirtualKeyboard(), + module_QtWebChannel(), + module_QtWebEngineCore(), + module_QtWebEngineQuick(), + module_QtWebEngineWidgets(), + module_QtWebSockets(), + ] + return files + + +# Functions that hold the information of all the files that needs +# to be included for the module to work, including Qt libraries, +# examples, typesystems, glue, etc. +def module_QtCore() -> ModuleData: + # QtCore + data = ModuleData("Core", examples=["corelib"]) + data.typesystems.append("core_common.xml") + data.typesystems.append("typesystem_core_common.xml") + data.typesystems.append("typesystem_core_win.xml") + data.include.append("*.h") + if sys.platform == "win32": + data.plugins.append("assetimporters") + data.plugins.append("styles") + data.qtlib.append("pyside6.*") + data.extra_files.append("qt.conf") + data.extra_files.append("uic.exe") + data.extra_files.append("rcc.exe") + data.extra_files.append("qtdiag.exe") + data.extra_files.append("d3dcompiler*") + data.extra_files.append("lconvert*") + data.extra_files.append("pyside6.*.lib") + data.extra_files.append("resources/icudtl.dat") + else: + data.lib.append("libpyside6.*") + if sys.platform == "darwin": + data.plugins.append("styles") + + data.examples.append("samplebinding") + data.examples.append("widgetbinding") + data.examples.append("scriptableapplication") + data.examples.append("utils") + data.examples.append("external") + data.examples.append("installer_test") + data.examples.append("macextras") + + # *.py + data.extra_dirs.append("support") + data.extra_dirs.append("scripts") + + data.extra_dirs.append("Qt/libexec/") + data.extra_dirs.append("typesystems/glue") + + data.extra_files.append("__feature__.pyi") + data.extra_files.append("__init__.py") + data.extra_files.append("_git_pyside_version.py") + data.extra_files.append("_config.py") + data.extra_files.append("py.typed") + + # Assistant + if sys.platform == "darwin": + data.extra_dirs.append("Assistant.app") + else: + data.extra_files.append("assistant*") + data.translations.append("assistant_*") + + # Designer + if sys.platform == "darwin": + data.extra_dirs.append("Designer.app") + else: + data.extra_files.append("designer*") + data.translations.append("designer_*") + + # Linguist + if sys.platform == "darwin": + data.extra_dirs.append("Linguist.app") + else: + data.extra_files.append("linguist*") + data.translations.append("linguist_*") + + data.extra_files.append("lrelease*") + data.extra_files.append("lupdate*") + data.extra_files.append("qmllint*") + + # General translations + data.translations.append("qtbase_*") + data.translations.append("qt_help_*") + data.translations.append("qt_*") + + data.extra_files.append("examples/examples.pyproject") + + # plugins + data.plugins.append("platforms") + data.plugins.append("platformthemes") + data.plugins.append("platforminputcontexts") + data.plugins.append("imageformats") + data.plugins.append("generic") + data.plugins.append("xcbglintegrations") + + # Extra libraries + data.qtlib.append("libicudata*") + data.qtlib.append("libicui18n*") + data.qtlib.append("libicule*") + data.qtlib.append("libiculx*") + data.qtlib.append("libicutest*") + data.qtlib.append("libicutu*") + data.qtlib.append("libicuuc*") + data.qtlib.append("libicuio*") + + return data + + +def module_QtGui() -> ModuleData: + data = ModuleData("Gui") + _typesystems = [ + "gui_common.xml", + "typesystem_gui_common.xml", + "typesystem_gui_mac.xml", + "typesystem_gui_win.xml", + "typesystem_gui_x11.xml", + ] + + _metatypes = [ + "qt6eglfsdeviceintegrationprivate_relwithdebinfo_metatypes.json", + "qt6eglfskmssupportprivate_relwithdebinfo_metatypes.json", + "qt6kmssupportprivate_relwithdebinfo_metatypes.json", + "qt6xcbqpaprivate_relwithdebinfo_metatypes.json", + ] + + _qtlib = [ + "libQt6EglFSDeviceIntegration", + "libQt6EglFsKmsSupport", + "libQt6XcbQpa", + ] + + data.typesystems.extend(_typesystems) + data.metatypes.extend(_metatypes) + data.qtlib.extend(_qtlib) + + data.plugins.append("egldeviceintegrations") + data.extra_files.append("Qt/plugins/platforms/libqeglfs*") + + return data + + +def module_QtWidgets() -> ModuleData: + data = ModuleData("Widgets") + data.typesystems.append("widgets_common.xml") + data.typesystems.append("typesystem_widgets_common.xml") + + return data + + +def module_QtHelp() -> ModuleData: + data = ModuleData("Help") + + return data + + +def module_QtNetwork() -> ModuleData: + data = ModuleData("Network") + data.plugins.append("networkinformation") + data.plugins.append("tls") + + return data + + +def module_QtBluetooth() -> ModuleData: + data = ModuleData("Bluetooth") + + return data + + +def module_QtConcurent() -> ModuleData: + data = ModuleData("Concurrent") + + return data + + +def module_QtDBus() -> ModuleData: + data = ModuleData("DBus") + + return data + + +def module_QtDesigner() -> ModuleData: + data = ModuleData("Designer") + data.qtlib.append("libQt6DesignerComponents") + data.metatypes.append("qt6designercomponentsprivate_relwithdebinfo_metatypes.json") + data.plugins.append("designer") + data.extra_files.append("Qt/plugins/assetimporters/libuip*") + + return data + + +def module_QtNfc() -> ModuleData: + data = ModuleData("Nfc") + + return data + + +def module_QtPrintSupport() -> ModuleData: + data = ModuleData("PrintSupport") + data.typesystems.append("typesystem_printsupport_common.xml") + data.plugins.append("printsupport") + + return data + + +def module_QtQml() -> ModuleData: + data = ModuleData("Qml") + + _qtlib = [ + "libQt6LabsAnimation", + "libQt6LabsFolderListModel", + "libQt6LabsQmlModels*", + "libQt6LabsSettings", + "libQt6LabsSharedImage", + "libQt6LabsWavefrontMesh", + "libQt6QmlCore", + "libQt6QmlLocalStorage", + "libQt6QmlModels", + "libQt6QmlWorkerScript", + "libQt6QmlXmlListModel", + ] + + _include = [ + "pysideqml.h", + "pysideqmlmacros.h", + "pysideqmlregistertype.h", + ] + + _metatypes = [ + "qt6labsanimation_relwithdebinfo_metatypes.json", + "qt6labsfolderlistmodel_relwithdebinfo_metatypes.json", + "qt6labsqmlmodels_relwithdebinfo_metatypes.json", + "qt6labssettings_relwithdebinfo_metatypes.json", + "qt6labssharedimage_relwithdebinfo_metatypes.json", + "qt6labswavefrontmesh_relwithdebinfo_metatypes.json", + "qt6packetprotocolprivate_relwithdebinfo_metatypes.json", + "qt6qmlcompilerprivate_relwithdebinfo_metatypes.json", + "qt6qmlcore_relwithdebinfo_metatypes.json", + "qt6qmldebugprivate_relwithdebinfo_metatypes.json", + "qt6qmldomprivate_relwithdebinfo_metatypes.json", + "qt6qmllintprivate_relwithdebinfo_metatypes.json", + "qt6qmllocalstorage_relwithdebinfo_metatypes.json", + "qt6qmlmodels_relwithdebinfo_metatypes.json", + "qt6qmlworkerscript_relwithdebinfo_metatypes.json", + "qt6qmlxmllistmodel_relwithdebinfo_metatypes.json", + ] + + _qml = [ + "Qt/labs/animation", + "Qt/labs/folderlistmodel", + "Qt/labs/sharedimage", + "Qt/labs/wavefrontmesh", + "Qt/labs/qmlmodels", + "Qt/labs/platform", + "Qt/labs/settings", + ] + + data.lib.append("libpyside6qml") + data.examples.append("declarative") + data.plugins.append("qmltooling") + data.translations.append("qtdeclarative_*") + if sys.platform == "win32": + data.extra_files.append("pyside6qml.*.lib") + data.extra_files.append("pyside6qml.*.dll") + data.extra_files.append("qml/builtins.qmltypes") + data.extra_files.append("qml/jsroot.qmltypes") + else: + data.extra_files.append("Qt/qml/builtins.qmltypes") + data.extra_files.append("Qt/qml/jsroot.qmltypes") + + data.qtlib.extend(_qtlib) + data.include.extend(_include) + data.metatypes.extend(_metatypes) + data.qml.extend(_qml) + + return data + + +def module_QtQuick() -> ModuleData: + data = ModuleData("Quick") + _metatypes = [ + "qt6quickcontrolstestutilsprivate_relwithdebinfo_metatypes.json", + "qt6quickdialogs2_relwithdebinfo_metatypes.json", + "qt6quickdialogs2quickimpl_relwithdebinfo_metatypes.json", + "qt6quickdialogs2utils_relwithdebinfo_metatypes.json", + "qt6quicketest_relwithdebinfo_metatypes.json", + "qt6quicketestutilsprivate_relwithdebinfo_metatypes.json", + "qt6quicklayouts_relwithdebinfo_metatypes.json", + "qt6quickparticlesprivate_relwithdebinfo_metatypes.json", + "qt6quickshapesprivate_relwithdebinfo_metatypes.json", + "qt6quicktemplates2_relwithdebinfo_metatypes.json", + "qt6quicktest_relwithdebinfo_metatypes.json", + "qt6quicktestutilsprivate_relwithdebinfo_metatypes.json", + "qt6quicktimeline_relwithdebinfo_metatypes.json", + ] + _qtlib = [ + "libQt6QuickDialogs2", + "libQt6QuickDialogs2QuickImpl", + "libQt6QuickDialogs2Utils", + "libQt6QuickLayouts", + "libQt6QuickParticles", + "libQt6QuickShapes", + "libQt6QuickTemplates2", + "libQt6QuickTest", + "libQt6QuickTimeline", + ] + + data.qtlib.extend(_qtlib) + data.metatypes.extend(_metatypes) + + return data + + +def module_QtQuickControls2() -> ModuleData: + data = ModuleData("QuickControls2") + data.qtlib.append("libQt6QuickControls2Impl") + data.metatypes.append("qt6quickcontrols2impl_relwithdebinfo_metatypes.json") + + return data + + +def module_QtQuickWidgets() -> ModuleData: + data = ModuleData("QuickWidgets") + return data + + +def module_QtXml() -> ModuleData: + data = ModuleData("Xml") + return data + + +def module_QtTest() -> ModuleData: + data = ModuleData("Test") + return data + + +def module_QtSql() -> ModuleData: + data = ModuleData("Sql") + data.plugins.append("sqldrivers") + + return data + + +def module_QtSvg() -> ModuleData: + data = ModuleData("Svg") + data.plugins.append("iconengines") + + return data + + +def module_QtSvgWidgets() -> ModuleData: + data = ModuleData("SvgWidgets") + + return data + + +def module_QtTextToSpeech() -> ModuleData: + data = ModuleData("TextToSpeech") + + return data + + +def module_QtUiTools() -> ModuleData: + data = ModuleData("UiTools") + + return data + + +def module_QtWayland() -> ModuleData: + data = ModuleData("Wayland") + + _qtlib = [ + "libQt6WaylandClient", + "libQt6WaylandCompositor", + "libQt6WaylandEglClientHwIntegration", + "libQt6WaylandEglCompositorHwIntegration", + "libQt6WlShellIntegration", + ] + + _metatypes = [ + "qt6waylandclient_relwithdebinfo_metatypes.json", + "qt6waylandeglclienthwintegrationprivate_relwithdebinfo_metatypes.json", + "qt6wlshellintegrationprivate_relwithdebinfo_metatypes.json", + ] + + # This is added by module_QtCore() + # data.plugins.append("platforms") + _plugins = [ + "wayland-decoration", + "wayland-decoration-client", + "wayland-graphics-integration-client", + "wayland-graphics-integration-server", + "wayland-shell-integration", + ] + + data.qtlib.extend(_qtlib) + data.metatypes.extend(_metatypes) + data.plugins.extend(_plugins) + return data + + +def module_Qt3DCore() -> ModuleData: + data = ModuleData("3DCore", qml=["Qt3D/Core"]) + + _plugins = [ + "geometryloaders", + "renderers", + "renderplugins", + "sceneparsers", + ] + + data.plugins.extend(_plugins) + data.examples.append("3d") + return data + + +def module_Qt3DAnimation() -> ModuleData: + data = ModuleData("3DAnimation", qml=["Qt3D/Animation"]) + + return data + + +def module_Qt3DExtras() -> ModuleData: + data = ModuleData("3DExtras", qml=["Qt3D/Extras"]) + + return data + + +def module_Qt3DInput() -> ModuleData: + data = ModuleData("3DInput", qml=["Qt3D/Input"]) + + return data + + +def module_Qt3DLogic() -> ModuleData: + data = ModuleData("3DLogic", qml=["Qt3D/Logic"]) + + return data + + +def module_Qt3DRender() -> ModuleData: + data = ModuleData("3DRender", qml=["Qt3D/Render"]) + + return data + + +def module_QtQuick3D() -> ModuleData: + data = ModuleData("Quick3D") + + _qtlib = [ + "libQt6Quick3DAssetImport", + "libQt6Quick3DAssetUtils", + "libQt6Quick3DEffects", + "libQt6Quick3DGlslParser", + "libQt6Quick3DHelpers", + "libQt6Quick3DIblBaker", + "libQt6Quick3DParticleEffects", + "libQt6Quick3DParticles", + "libQt6Quick3DRuntimeRender", + "libQt6Quick3DUtils", + "libQt6ShaderTools", + "libQt63DQuick", + "libQt63DQuickAnimation", + "libQt63DQuickExtras", + "libQt63DQuickExtras", + "libQt63DQuickInput", + "libQt63DQuickRender", + "libQt63DQuickScene2D", + ] + + _metatypes = [ + "qt63dquick_relwithdebinfo_metatypes.json", + "qt63dquickanimation_relwithdebinfo_metatypes.json", + "qt63dquickextras_relwithdebinfo_metatypes.json", + "qt63dquickinput_relwithdebinfo_metatypes.json", + "qt63dquickrender_relwithdebinfo_metatypes.json", + "qt63dquickscene2d_relwithdebinfo_metatypes.json", + "qt6quick3dassetimport_relwithdebinfo_metatypes.json", + "qt6quick3dassetutils_relwithdebinfo_metatypes.json", + "qt6quick3deffects_relwithdebinfo_metatypes.json", + "qt6quick3dglslparserprivate_relwithdebinfo_metatypes.json", + "qt6quick3dhelpers_relwithdebinfo_metatypes.json", + "qt6quick3diblbaker_relwithdebinfo_metatypes.json", + "qt6quick3dparticleeffects_relwithdebinfo_metatypes.json", + "qt6quick3dparticles_relwithdebinfo_metatypes.json", + "qt6quick3druntimerender_relwithdebinfo_metatypes.json", + "qt6quick3dutils_relwithdebinfo_metatypes.json", + "qt6shadertools_relwithdebinfo_metatypes.json", + ] + + data.qtlib.extend(_qtlib) + data.metatypes.extend(_metatypes) + data.extra_files.append("Qt/plugins/assetimporters/libassimp*") + + return data + + +def module_QtAxContainer() -> ModuleData: + data = ModuleData("AxContainer") + if sys.platform == "win32": + data.metatypes.append("qt6axbaseprivate_metatypes.json") + data.metatypes.append("qt6axserver_metatypes.json") + + return data + + +def module_QtWebEngineCore() -> ModuleData: + data = ModuleData("WebEngineCore", qml=["QtWebEngine"]) + data.translations.append("qtwebengine_locales/*") + data.translations.append("qtwebengine_*") + data.extra_dirs.append("Qt/resources") + if sys.platform == "win32": + data.extra_files.append("resources/qtwebengine*.pak") + data.extra_files.append("QtWebEngineProcess.exe") + + return data + + +def module_QtWebEngineWidgets() -> ModuleData: + data = ModuleData("WebEngineWidgets") + + return data + + +def module_QtWebEngineQuick() -> ModuleData: + data = ModuleData("WebEngineQuick") + data.qtlib.append("libQt6WebEngineQuickDelegatesQml") + data.metatypes.append("qt6webenginequickdelegatesqml_relwithdebinfo_metatypes.json") + + return data + + +def module_QtCharts() -> ModuleData: + data = ModuleData("Charts") + data.qtlib.append("libQt6ChartsQml") + data.metatypes.append("qt6chartsqml_relwithdebinfo_metatypes.json") + + return data + + +def module_QtDataVisualization() -> ModuleData: + data = ModuleData("DataVisualization") + data.qtlib.append("libQt6DataVisualizationQml") + data.metatypes.append("qt6datavisualizationqml_relwithdebinfo_metatypes.json") + data.typesystems.append("datavisualization_common.xml") + + return data + + +def module_QtMultimedia() -> ModuleData: + data = ModuleData("Multimedia") + data.qtlib.append("libQt6MultimediaQuick") + data.metatypes.append("qt6multimediaquickprivate_relwithdebinfo_metatypes.json") + data.translations.append("qtmultimedia_*") + + return data + + +def module_QtMultimediaWidgets() -> ModuleData: + data = ModuleData("MultimediaWidgets") + + return data + + +def module_QtNetworkAuth() -> ModuleData: + data = ModuleData("NetworkAuth") + + return data + + +def module_QtPositioning() -> ModuleData: + data = ModuleData("Positioning") + data.qtlib.append("libQt6PositioningQuick") + data.metatypes.append("qt6positioningquick_relwithdebinfo_metatypes.json") + data.plugins.append("position") + + return data + + +def module_QtRemoteObjects() -> ModuleData: + data = ModuleData("RemoteObjects") + data.qtlib.append("libQt6RemoteObjectsQml") + data.metatypes.append("qt6remoteobjectsqml_relwithdebinfo_metatypes.json") + + return data + + +def module_QtSensors() -> ModuleData: + data = ModuleData("Sensors") + data.qtlib.append("libQt6SensorsQuick") + data.metatypes.append("qt6sensorsquick_relwithdebinfo_metatypes.json") + data.plugins.append("sensors") + + return data + + +def module_QtSerialPort() -> ModuleData: + data = ModuleData("SerialPort") + data.translations.append("qtserialport_*") + + return data + + +def module_QtStateMachine() -> ModuleData: + data = ModuleData("StateMachine") + data.qtlib.append("libQt6StateMachineQml") + data.metatypes.append("qt6statemachineqml_relwithdebinfo_metatypes.json") + + return data + + +def module_QtScxml() -> ModuleData: + data = ModuleData("Scxml") + data.qtlib.append("libQt6ScxmlQml") + data.metatypes.append("qt6scxmlqml_relwithdebinfo_metatypes.json") + data.plugins.append("scxmldatamodel") + + return data + + +def module_QtWebChannel() -> ModuleData: + data = ModuleData("WebChannel") + + return data + + +def module_QtWebSockets() -> ModuleData: + data = ModuleData("WebSockets") + data.translations.append("qtwebsockets_*") + + return data + + +def module_QtOpenGL() -> ModuleData: + data = ModuleData("OpenGL") + _typesystems = [ + "opengl_common.xml", + "typesystem_opengl_modifications1_0.xml", + "typesystem_opengl_modifications1_0_compat.xml", + "typesystem_opengl_modifications1_1.xml", + "typesystem_opengl_modifications1_1_compat.xml", + "typesystem_opengl_modifications1_2_compat.xml", + "typesystem_opengl_modifications1_3_compat.xml", + "typesystem_opengl_modifications1_4.xml", + "typesystem_opengl_modifications1_4_compat.xml", + "typesystem_opengl_modifications2_0.xml", + "typesystem_opengl_modifications2_0_compat.xml", + "typesystem_opengl_modifications2_1.xml", + "typesystem_opengl_modifications3_0.xml", + "typesystem_opengl_modifications3_3.xml", + "typesystem_opengl_modifications3_3a.xml", + "typesystem_opengl_modifications4_0.xml", + "typesystem_opengl_modifications4_1.xml", + "typesystem_opengl_modifications4_3.xml", + "typesystem_opengl_modifications4_4.xml", + "typesystem_opengl_modifications4_4_core.xml", + "typesystem_opengl_modifications4_5.xml", + "typesystem_opengl_modifications4_5_core.xml", + "typesystem_opengl_modifications_va.xml", + ] + + data.typesystems.extend(_typesystems) + if sys.platform == "win32": + data.extra_files.append("opengl32*.dll") + + return data + + +def module_QtOpenGLWidgets() -> ModuleData: + data = ModuleData("OpenGLWidgets") + + return data + + +def module_QtVirtualKeyboard() -> ModuleData: + data = ModuleData("VirtualKeyboard") + data.plugins.append("virtualkeyboard") + + return data diff --git a/coin_build_instructions.py b/coin_build_instructions.py index 43f789a2e..ba281eb4d 100644 --- a/coin_build_instructions.py +++ b/coin_build_instructions.py @@ -140,7 +140,7 @@ def call_setup(python_ver, phase): cmd = [env_python, "-u", "setup.py"] if phase in ["BUILD"]: - cmd += ["build", "--standalone", "--skip-packaging"] + cmd += ["build", "--standalone"] elif phase in ["WHEEL"] or CI_RELEASE_CONF: cmd += ["bdist_wheel", "--reuse-build", "--standalone", "--skip-cmake", "--skip-make-install", "--only-package"] @@ -153,8 +153,8 @@ def call_setup(python_ver, phase): if CI_USE_SCCACHE: cmd += [f"--compiler-launcher={CI_USE_SCCACHE}"] - if python_ver == "3": - cmd += ["--limited-api=yes"] + cmd += ["--limited-api=yes"] + if is_snapshot_build(): cmd += ["--snapshot-build"] @@ -173,6 +173,10 @@ def call_setup(python_ver, phase): env = os.environ run_instruction(cmd, "Failed to run setup.py for build", initial_env=env) + if phase in ["WHEEL"] or CI_RELEASE_CONF: + cmd = [env_python, "create_wheels.py"] + run_instruction(cmd, "Failed to create new wheels", initial_env=env) + if __name__ == "__main__": # Remove some environment variables that impact cmake diff --git a/coin_test_instructions.py b/coin_test_instructions.py index 732a5ba86..dfb2029b2 100644 --- a/coin_test_instructions.py +++ b/coin_test_instructions.py @@ -106,8 +106,17 @@ def call_testrunner(python_ver, buildnro): # Try to install built wheels, and build some buildable examples. if CI_RELEASE_CONF: wheel_tester_path = os.path.join("testing", "wheel_tester.py") + # Run the test for the old set of wheels cmd = [env_python, wheel_tester_path, qmake_path] - run_instruction(cmd, "Error while running wheel_tester.py") + run_instruction(cmd, "Error while running wheel_tester.py on old wheels") + + # Uninstalling the other wheels + run_instruction([env_pip, "uninstall", "shiboken6", "shiboken6_generator", "pyside6", "-y"], + "Failed to uninstall old wheels") + + # Run the test for the new set of wheels + cmd = [env_python, wheel_tester_path, qmake_path, "--wheels-dir=dist_new", "--new"] + run_instruction(cmd, "Error while running wheel_tester.py on new wheels") def run_test_instructions(): # Remove some environment variables that impact cmake diff --git a/create_wheels.py b/create_wheels.py new file mode 100644 index 000000000..ff350f451 --- /dev/null +++ b/create_wheels.py @@ -0,0 +1,417 @@ +############################################################################# +## +## Copyright (C) 2022 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import os +import platform +import sys +from argparse import ArgumentParser, Namespace +from dataclasses import dataclass +from pathlib import Path +from shutil import copy, rmtree +from sysconfig import get_config_var +from typing import List, Optional, Tuple + +import build # type: ignore +from build_scripts.wheel_files import (ModuleData, # type: ignore + wheel_files_pyside_addons, + wheel_files_pyside_essentials) + + +@dataclass +class SetupData: + name: str + version: str + description: str + long_description: str + console_scripts: List[str] + + +def get_version_from_package(name: str) -> str: + # Get version from the already configured '__init__.py' file + version = "" + with open(package_path / name / "__init__.py") as f: + for line in f: + if line.strip().startswith("__version__"): + version = line.split("=")[1].strip().replace('"', "") + break + return version + + +def get_manifest(wheel_name: str, data: List[ModuleData]) -> str: + lines = [] + + for module in data: + # It's crucial to have this adjust method here + # because it include all the necessary modifications to make + # our soltuion work on the three main platforms. + module.adjusts_paths_and_extensions() + + for field in module.get_fields(): + if field == "name": + lines.append(f"# {getattr(module, field)}") + continue + if field == "ext": + continue + for line in getattr(module, field): + if field in ("examples", "extra_dirs", "qml", "plugins"): + lines.append(f"graft PySide6/{line}") + elif field == "qtlib" and sys.platform == "darwin": + lines.append(f"graft PySide6/{line}") + else: + lines.append(f"include PySide6/{line}") + lines.append("recursive-exclude PySide6 *qt.conf*") + lines.append("") + + # Skip certain files if needed + lines.append("recursive-exclude PySide6/Qt/qml *.debug") + + return "\n".join(lines) + + +def generate_setup_cfg(artifacts: Path, setup: SetupData) -> str: + content = None + _os = sys.platform + arch = platform.machine() + + # as Qt6 we know it's GLIBC 2.28 on RHEL 8.4 + _tag = "" + if _os == "linux": + glibc = platform.libc_ver()[1].replace(".", "_") + # Will generate manylinux_2_28_x86_64 + _tag = f"manylinux_{glibc}_{arch}" + elif _os == "darwin": + target = get_config_var("MACOSX_DEPLOYMENT_TARGET") + if not target: + print("Error: couldn't get the value from MACOSX_DEPLOYMENT_TARGET. " + "Falling back to local platform version.") + mac_ver, _, _ = platform.mac_ver() + # We get 10.14.2 for example, and transform into 10_14 + target = "_".join(mac_ver.split(".")[:2]) + else: + version = target.split(".") + if len(version) > 1: + target = "_".join(version) + else: + target = f"{version[0]}_0" + # TODO: Make it general + # To check if is compatible with 64bit on multi-arch systems + # is_64bits = sys.maxsize > 2**32 + # + # We know the CI builds universal2 wheels + _tag = f"macosx_{target}_universal2" + elif _os == "win32": + win_arch = platform.architecture()[0] + msvc_arch = "x86" if win_arch.startswith("32") else "amd64" + _tag = f"win_{msvc_arch}" + + with open(artifacts / "setup.cfg.base") as f: + content = f.read().format( + name=setup.name, + version=setup.version, + description=setup.description, + long_description=setup.long_description, + tag=_tag, + ) + + return content + + +def generate_setup_py(artifacts: Path, setup: SetupData): + content = None + _name = setup.name + + # To get the 'abi3' tag on the wheel name, we need to use + # a fake extension to activate the limited-api option. + # Because of the order of copying, we will use a name of a real + # module for each package, so it gets overwrited. + if _name == "PySide6": + fext = "PySide6/QtCore" + elif _name == "PySide6_Addons": + fext = "PySide6/Qt3DCore" + else: + fext = "Shiboken" + + # Installing dependencies + install_requires = [] + if name == "PySide6": + install_requires.append(f"shiboken6=={setup.version}") + install_requires.append(f"PySide6_Essentials=={setup.version}") + install_requires.append(f"PySide6_Addons=={setup.version}") + elif _name == "PySide6_Essentials": + install_requires.append(f"shiboken6=={setup.version}") + elif _name == "PySide6_Addons": + install_requires.append(f"shiboken6=={setup.version}") + install_requires.append(f"PySide6_Essentials=={setup.version}") + + # For special wheels based on 'PySide6' + # we force the name to be PySide6 for the package_name, + # so we can take the files from that packaged-directory + if setup.name in ("PySide6_Essentials", "PySide6_Addons"): + _name = "PySide6" + + with open(artifacts / "setup.py.base") as f: + content = f.read().format( + name=_name, + fake_ext=fext, + install=install_requires, + console_scripts={"console_scripts": setup.console_scripts}, + ) + + return content + + +def wheel_shiboken_generator() -> Tuple[SetupData, None]: + setup = SetupData( + name="shiboken6_generator", + version=get_version_from_package("shiboken6_generator"), + description="Python/C++ bindings generator", + long_description="README.shiboken6-generator.md", + console_scripts=[ + "shiboken6 = shiboken6_generator.scripts.shiboken_tool:main", + "shiboken6-genpyi = shiboken6_generator.scripts.shiboken_tool:genpyi", + ], + ) + + return setup, None + + +def wheel_shiboken_module() -> Tuple[SetupData, None]: + setup = SetupData( + name="shiboken6", + version=get_version_from_package("shiboken6"), + description="Python/C++ bindings helper module", + long_description="README.shiboken6.md", + console_scripts=[], + ) + + return setup, None + + +def wheel_pyside6_essentials() -> Tuple[SetupData, List[ModuleData]]: + setup = SetupData( + name="PySide6_Essentials", + version=get_version_from_package("PySide6"), # we use 'PySide6' here + description="Python bindings for the Qt cross-platform application and UI framework (Essentials)", + long_description="README.pyside6_essentials.md", + console_scripts=[ + "pyside6-uic = PySide6.scripts.pyside_tool:uic", + "pyside6-rcc = PySide6.scripts.pyside_tool:rcc", + "pyside6-assistant = PySide6.scripts.pyside_tool:assistant", + "pyside6-designer= PySide6.scripts.pyside_tool:designer", + "pyside6-linguist = PySide6.scripts.pyside_tool:linguist", + "pyside6-lupdate = PySide6.scripts.pyside_tool:lupdate", + "pyside6-lrelease = PySide6.scripts.pyside_tool:lrelease", + "pyside6-genpyi = PySide6.scripts.pyside_tool:genpyi", + "pyside6-metaobjectdump = PySide6.scripts.pyside_tool:metaobjectdump", + "pyside6-qmltyperegistrar = PySide6.scripts.pyside_tool:qmltyperegistrar", + "pyside6-qmllint = PySide6.scripts.pyside_tool:qmllint", + ], + ) + + data = wheel_files_pyside_essentials() + + return setup, data + + +def wheel_pyside6_addons() -> Tuple[SetupData, List[ModuleData]]: + setup = SetupData( + name="PySide6_Addons", + version=get_version_from_package("PySide6"), # we use 'PySide6' here + description="Python bindings for the Qt cross-platform application and UI framework (Addons)", + long_description="README.pyside6_addons.md", + console_scripts=[], + ) + + data = wheel_files_pyside_addons() + + return setup, data + + +def wheel_pyside6() -> Tuple[SetupData, Optional[List[ModuleData]]]: + setup = SetupData( + name="PySide6", + version=get_version_from_package("PySide6"), + description="Python bindings for the Qt cross-platform application and UI framework", + long_description="README.pyside6.md", + console_scripts=[], + ) + + return setup, None + + +def get_build_directory(options: Namespace): + _venv = "" + _directories = list(Path("build").glob("qfp*")) + # Search for a "--env" option first" + if options.env is not None: + _venv = f"{options.env}a" + # Search for a 'qfp' directory second + elif _directories and len(_directories) > 0: + # Take the first 'qfp' directory + _venv = _directories[0].name + # Fall back to the virtual environment name + else: + # Check if we are using a virtual environment + try: + _venv = os.environ["VIRTUAL_ENV"] + if not _venv: + raise Exception("No virtual environment found") + _venv = f"{_venv}a" + except Exception as e: + print(f"{type(e).__name__} : {e}") + sys.exit(-1) + + return Path(_venv) + + +if __name__ == "__main__": + + # Command line option to find the build/<envname>a/package_for_wheels + parser = ArgumentParser() + parser.add_argument("--env", type=str, default=None) + options = parser.parse_args() + + venv = get_build_directory(options) + + verbose = False + # Setup paths + current_path = Path(__file__).resolve().parent + artifacts_path = Path("wheel_artifacts/") + # the extra 'a' is for compatibility with the build_scripts + # notation that adds an 'a' when using limited-api + package_path = Path("build") / venv.name / "package_for_wheels" + + # Check for 'package_for_wheels' directory + if not package_path.is_dir(): + print(f"Couldn't find the directory: {package_path}") + print("Maybe your build used '--skip-packaging'?. Exiting") + sys.exit(-1) + + setup_cfg_path = package_path / "setup.cfg" + setup_py_path = package_path / "setup.py" + + base_files = [ + artifacts_path / "pyproject.toml", + current_path / "LICENSE.COMMERCIAL", + current_path / "LICENSE.FDL", + current_path / "LICENSE.GPL2", + current_path / "LICENSE.GPLv3", + current_path / "LICENSE.GPLv3-EXCEPT", + current_path / "LICENSE.LGPLv3", + ] + + # Main generation + wheels = { + "shiboken6": wheel_shiboken_module, + "shiboken6_generator": wheel_shiboken_generator, + "PySide6_Essentials": wheel_pyside6_essentials, + "PySide6_Addons": wheel_pyside6_addons, + "PySide6": wheel_pyside6, + } + + for name, wheel_info in wheels.items(): + + print(f"Starting process for: {name}") + setup, data = wheel_info() + + # 1. Generate 'setup.cfg' + print("-- Generating setup.cfg") + setup_cfg_content = generate_setup_cfg(artifacts_path, setup) + with open(setup_cfg_path, "w") as f: + f.write(setup_cfg_content) + + # 2. Generate 'setup.py' + print("-- Generating setup.py") + setup_py_content = generate_setup_py(artifacts_path, setup) + with open(setup_py_path, "w") as f: + f.write(setup_py_content) + + # 3. Create the 'MANIFEST.in' + # Special case for shiboken and shiboken_generator + # so we copy the whole directory, only PySide and derivatives + # will need to have specific information + print("-- Creating MANIFEST.in") + if not data: + if name == "PySide6": + with open(package_path / "MANIFEST.in", "w") as f: + f.write(f"purge {name}\n") + else: + with open(package_path / "MANIFEST.in", "w") as f: + f.write(f"graft {name}\n") + else: + manifest_content = get_manifest(name, data) + with open(package_path / "MANIFEST.in", "w") as f: + f.write(manifest_content) + + # 4. copy configuration files to create the wheel + print("-- Copy configuration files to create the wheel") + _files: List[Path] = base_files + [Path(setup.long_description)] + for fname in _files: + copy(fname, package_path) + + # 5. call the build module to create the wheel + # print("-- Creating wheel") + # os.chdir(package_path) + if not verbose: + _runner = build.pep517.wrappers.quiet_subprocess_runner + else: + _runner = build.pep517.wrappers.default_subprocess_runner + builder = build.ProjectBuilder(package_path, runner=_runner) + builder.build("wheel", "dist_new") + # os.chdir(current_path) + + # 6. Copy wheels back + print("-- Copying wheels to dist_new/") + dist_path = Path("dist_new") + if not dist_path.is_dir(): + dist_path.mkdir() + for wheel in Path(package_path / "dist_new").glob("*.whl"): + copy(wheel, dist_path / wheel.name) + + # 7. Remove leftover files + print("-- Removing leftover files") + all_files = set(package_path.glob("*")) + files_to_remove = all_files - { + package_path / i for i in ("PySide6", "shiboken6", "shiboken6_generator") + } + for _f in files_to_remove: + if _f.is_dir(): + rmtree(_f) + elif _f.is_file(): + _f.unlink() diff --git a/requirements.txt b/requirements.txt index a2b45fbc5..e1671d0d9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ setuptools>=48.0 +build==0.7 sphinx sphinx-panels six @@ -6,3 +7,4 @@ wheel>=0.35 PyOpenGL pyinstaller==3.6 nuitka +dataclasses; python_version < '3.7' diff --git a/testing/wheel_tester.py b/testing/wheel_tester.py index db36aa45e..c97647861 100644 --- a/testing/wheel_tester.py +++ b/testing/wheel_tester.py @@ -76,6 +76,7 @@ import platform log.set_verbosity(1) +NEW_WHEELS = False def find_executable(executable, command_line_value): value = command_line_value @@ -104,8 +105,8 @@ QMAKE_PATH = None CMAKE_PATH = None -def get_wheels_dir(): - return os.path.join(setup_script_dir, "dist") +def get_wheels_dir(dir_name): + return os.path.join(setup_script_dir, dir_name) def get_examples_dir(): @@ -115,7 +116,10 @@ def get_examples_dir(): def package_prefix_names(): # Note: shiboken6_generator is not needed for compile_using_nuitka, # but building modules with cmake needs it. - return ["shiboken6", "shiboken6_generator", "PySide6"] + if NEW_WHEELS: + return ["shiboken6", "shiboken6_generator", "PySide6_Essentials", "PySide6_Addons", "PySide6"] + else: + return ["shiboken6", "shiboken6_generator", "PySide6"] def clean_egg_info(): @@ -334,8 +338,8 @@ def try_build_examples(): execute_script(src_path / f"{modname}.pyi") -def run_wheel_tests(install_wheels): - wheels_dir = get_wheels_dir() +def run_wheel_tests(install_wheels, wheels_dir_name): + wheels_dir = get_wheels_dir(wheels_dir_name) py_version = f"{sys.version_info.major}.{sys.version_info.minor}" if install_wheels: @@ -358,8 +362,11 @@ if __name__ == "__main__": ) parser.add_argument("--qmake", type=str, help="Path to qmake") parser.add_argument("--cmake", type=str, help="Path to cmake") + parser.add_argument("--wheels-dir", type=str, help="Path to where the wheels are", default="dist") + parser.add_argument("--new", action="store_true", help="Option to test new wheels") options = parser.parse_args() QMAKE_PATH = find_executable("qmake", options.qmake) CMAKE_PATH = find_executable("cmake", options.cmake) + NEW_WHEELS = options.new - run_wheel_tests(not options.no_install_wheels) + run_wheel_tests(not options.no_install_wheels, options.wheels_dir) diff --git a/wheel_artifacts/pyproject.toml b/wheel_artifacts/pyproject.toml new file mode 100644 index 000000000..9787c3bdf --- /dev/null +++ b/wheel_artifacts/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/wheel_artifacts/setup.cfg.base b/wheel_artifacts/setup.cfg.base new file mode 100644 index 000000000..9c8b60940 --- /dev/null +++ b/wheel_artifacts/setup.cfg.base @@ -0,0 +1,52 @@ +[metadata] +name = {name} +version = {version} +description = {description} +url = https://www.pyside.org +download_url = https://download.qt.io/official_releases/QtForPython +license = LGPL +keywords = Qt +author = Qt for Python Team +author_email = pyside@qt-project.org +long_description = file: {long_description} +long_description_content_type = text/markdown +ext_modules = None +projects_urls = + Bug Tracker = https://bugreports.qt.io +classifiers = + Development Status :: 5 - Production/Stable + Environment :: Console + Environment :: MacOS X + Environment :: X11 Applications :: Qt + Environment :: Win32 (MS Windows) + Intended Audience :: Developers + License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) + License :: Other/Proprietary License + Operating System :: MacOS :: MacOS X + Operating System :: POSIX + Operating System :: POSIX :: Linux + Operating System :: Microsoft + Operating System :: Microsoft :: Windows + Programming Language :: C++ + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Topic :: Database + Topic :: Software Development + Topic :: Software Development :: Code Generators + Topic :: Software Development :: Libraries :: Application Frameworks + Topic :: Software Development :: User Interfaces + Topic :: Software Development :: Widget Sets + +[options] +packages = find: +python_requires = >=3.6, <3.11 +include_package_data = True + +[bdist_wheel] +py_limited_api = cp36 +plat_name = {tag} diff --git a/wheel_artifacts/setup.py.base b/wheel_artifacts/setup.py.base new file mode 100644 index 000000000..18634cbf8 --- /dev/null +++ b/wheel_artifacts/setup.py.base @@ -0,0 +1,29 @@ +import setuptools +from setuptools import setup, Extension +from setuptools._distutils import cmd + +# This class and Extension file is intended only to force setuptools +# to understand we are using extension modules, but because we don't +# include the source files in the 'Extension' object, it gets wrongly +# lost. +class build_ext(cmd.Command): + def initialize_options(self): + pass + def finalize_options(self): + pass + def run(self): + pass + def get_source_files(self): + return [] + def get_requires_for_build_wheel(self): + pass + +setup_args = dict( + include_package_data=True, + packages = ["{name}"], + entry_points = {console_scripts}, + ext_modules = [Extension("{fake_ext}", [], py_limited_api=True)], + install_requires={install}, + cmdclass=dict([("build_ext", build_ext)]), +) +setup(**setup_args) |
