[neon/forks/pyqt-builder/Neon/release] /: New upstream version 1.12.1+dfsg

Dmitry Shachnev null at kde.org
Mon Oct 18 17:21:13 BST 2021


Git commit 58a5063a5b3c1a58cfc4a389a43d2c62924c193d by Dmitry Shachnev.
Committed on 15/10/2021 at 16:37.
Pushed by jriddell into branch 'Neon/release'.

New upstream version 1.12.1+dfsg

M  +32   -0    ChangeLog
M  +9    -0    NEWS
M  +1    -1    PKG-INFO
M  +1    -1    PyQt_builder.egg-info/PKG-INFO
M  +1    -1    PyQt_builder.egg-info/SOURCES.txt
M  +1    -1    PyQt_builder.egg-info/requires.txt
M  +5    -0    doc/pyqtbundle.rst
M  +9    -1    pyqtbuild/builder.py
M  +25   -27   pyqtbuild/bundle/abstract_package.py
M  +16   -6    pyqtbuild/bundle/bundle.py
M  +12   -2    pyqtbuild/bundle/bundle_main.py
M  +5    -5    pyqtbuild/bundle/packages/pyqt.py
M  +119  -64   pyqtbuild/bundle/qt_metadata.py
M  +22   -11   pyqtbuild/bundle/qt_wheel.py
M  +11   -1    pyqtbuild/bundle/qt_wheel_main.py
M  +13   -0    pyqtbuild/project.py
M  +2    -2    pyqtbuild/version.py
M  +1    -1    setup.py

https://invent.kde.org/neon/forks/pyqt-builder/commit/58a5063a5b3c1a58cfc4a389a43d2c62924c193d

diff --git a/ChangeLog b/ChangeLog
index 828e07f..384e8f3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,35 @@
+2021-10-12  Phil Thompson  <phil at riverbankcomputing.com>
+
+	* NEWS, pyqtbuild/bundle/bundle.py:
+	Fixed a syntax error in pyqt-bundle.
+	[1b94d1df9598] [1.12.1] <1.12-maint>
+
+	* .hgtags:
+	Added tag 1.12.0 for changeset a8e714b1af6a
+	[fdaf18dae011]
+
+2021-10-11  Phil Thompson  <phil at riverbankcomputing.com>
+
+	* NEWS, doc/pyqtbundle.rst, pyqtbuild/bundle/abstract_package.py,
+	pyqtbuild/bundle/bundle.py, pyqtbuild/bundle/bundle_main.py,
+	pyqtbuild/bundle/packages/pyqt.py, pyqtbuild/bundle/qt_metadata.py,
+	pyqtbuild/bundle/qt_wheel.py, pyqtbuild/bundle/qt_wheel_main.py:
+	Added support for the --arch option to pyqt-bundle and pyqt-qt-
+	wheel.
+	[a8e714b1af6a] [1.12.0]
+
+2021-10-05  Phil Thompson  <phil at riverbankcomputing.com>
+
+	* NEWS, pyqtbuild/builder.py, pyqtbuild/project.py, setup.py:
+	Build universal2 binaries when using Python 3.10 and Qt v6.2.
+	[2cbfd9cfac73]
+
+2021-09-30  Phil Thompson  <phil at riverbankcomputing.com>
+
+	* .hgtags:
+	Added tag 1.11.0 for changeset 128fd244a822
+	[543a47e6bc23]
+
 2021-09-25  Phil Thompson  <phil at riverbankcomputing.com>
 
 	* NEWS, pyqtbuild/bundle/packages/pyqt6_webengine.py:
diff --git a/NEWS b/NEWS
index 880f0b5..a5f43f1 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,12 @@
+v1.12.1 11th October 2021
+  - A bug fix release.
+
+v1.12.0 11th October 2021
+  - Build universal2 wheels for Qt v6.2 and later when using Python v3.10 and
+    later.
+  - Added the '--arch' command line option to pyqt-bundle to specify a
+    particular architecture (x86_64 or arm64) to bundle on macOS.
+
 v1.11.0 25th September 2021
   - Added support for bundling Qt v6.2.
   - Added support for PyQt6-WebEngine to pyqt-bundle.
diff --git a/PKG-INFO b/PKG-INFO
index 6cfc961..e278aa6 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: PyQt-builder
-Version: 1.11.0
+Version: 1.12.1
 Summary: The PEP 517 compliant PyQt build system
 Home-page: https://www.riverbankcomputing.com/software/pyqt-builder/
 Author: Riverbank Computing Limited
diff --git a/PyQt_builder.egg-info/PKG-INFO b/PyQt_builder.egg-info/PKG-INFO
index 6cfc961..e278aa6 100644
--- a/PyQt_builder.egg-info/PKG-INFO
+++ b/PyQt_builder.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: PyQt-builder
-Version: 1.11.0
+Version: 1.12.1
 Summary: The PEP 517 compliant PyQt build system
 Home-page: https://www.riverbankcomputing.com/software/pyqt-builder/
 Author: Riverbank Computing Limited
diff --git a/PyQt_builder.egg-info/SOURCES.txt b/PyQt_builder.egg-info/SOURCES.txt
index d816489..fb41b97 100644
--- a/PyQt_builder.egg-info/SOURCES.txt
+++ b/PyQt_builder.egg-info/SOURCES.txt
@@ -59,7 +59,7 @@ doc/html/_static/pygments.css
 doc/html/_static/riverbank.css
 doc/html/_static/searchtools.js
 doc/html/_static/sidebar.js
-doc/html/_static/underscore-1.3.1.js
+doc/html/_static/underscore-1.13.1.js
 doc/html/_static/underscore.js
 doc/riverbank/layout.html
 doc/riverbank/theme.conf
diff --git a/PyQt_builder.egg-info/requires.txt b/PyQt_builder.egg-info/requires.txt
index a2c1e15..9b2bbb0 100644
--- a/PyQt_builder.egg-info/requires.txt
+++ b/PyQt_builder.egg-info/requires.txt
@@ -1,2 +1,2 @@
 packaging
-sip<7,>=5.5
+sip<7,>=6.3
diff --git a/doc/pyqtbundle.rst b/doc/pyqtbundle.rst
index 4f42de2..154e0e0 100644
--- a/doc/pyqtbundle.rst
+++ b/doc/pyqtbundle.rst
@@ -66,6 +66,11 @@ The full set of command line options is:
     ``SUFFIX`` is appended to the build tag in the name of the updated wheel.
     The build tag is the version number of the copy of Qt being bundled.
 
+.. option:: --arch ARCH
+
+    On macOS, when bundling Qt v6.2 or later, support for the ``ARCH``
+    architecture (either ``x86_64`` or ``arm64``) only is included.
+
 .. option:: --exclude NAME
 
     The ``NAME`` bindings are excluded from the wheel.  This option may be
diff --git a/pyqtbuild/builder.py b/pyqtbuild/builder.py
index ad9275e..c3934e0 100644
--- a/pyqtbuild/builder.py
+++ b/pyqtbuild/builder.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2020, Riverbank Computing Limited
+# Copyright (c) 2021, Riverbank Computing Limited
 # All rights reserved.
 #
 # This copy of PyQt-builder is licensed for use under the terms of the SIP
@@ -24,6 +24,7 @@
 
 import os
 import sys
+import sysconfig
 
 from sipbuild import (Buildable, BuildableModule, Builder, Option, Project,
         PyProjectOptionException, UserException)
@@ -679,6 +680,13 @@ macx {
 
         buildable.make_names_relative()
 
+        # Handle the target architecture(s).  The way that Python handles
+        # 'universal2' seems broken as it is determined by what versions of
+        # macOS and the SDK the interpreter was built against rather than the
+        # versions that are being used now.
+        if self.project.get_platform_tag().endswith('_universal2'):
+            pro_lines.append('QMAKE_APPLE_DEVICE_ARCHS = x86_64 arm64')
+
         # Handle debugging.
         pro_lines.append(
                 'CONFIG += {}'.format(
diff --git a/pyqtbuild/bundle/abstract_package.py b/pyqtbuild/bundle/abstract_package.py
index 9f0c1b3..c539064 100644
--- a/pyqtbuild/bundle/abstract_package.py
+++ b/pyqtbuild/bundle/abstract_package.py
@@ -62,34 +62,32 @@ class AbstractPackage(ABC):
         else:
             self._version = None
 
-    def bundle_msvc_runtime(self, target_qt_dir, arch):
+    def bundle_msvc_runtime(self, target_qt_dir, platform_tag):
         """ Bundle the MSVC runtime. """
 
         # This default implementation does nothing.
 
-    def bundle_openssl(self, target_qt_dir, openssl_dir, arch):
+    def bundle_openssl(self, target_qt_dir, openssl_dir, platform_tag):
         """ Bundle the OpenSSL DLLs. """
 
         # This default implementation does nothing.
 
-    def bundle_qt(self, target_qt_dir, arch, exclude, ignore_missing,
+    def bundle_qt(self, target_qt_dir, platform_tag, exclude, ignore_missing,
             bindings=True):
         """ Bundle the relevant parts of the Qt installation.  Returns True if
         the LGPL applies to all bundled parts.
         """
 
         # Architecture-specific values.
-        if arch.startswith('manylinux'):
-            metadata_arch = 'linux'
+        if platform_tag.startswith('manylinux'):
             module_extensions = ['.abi3.so', '.so']
-        elif arch.startswith('macosx'):
-            metadata_arch = 'macos'
+        elif platform_tag.startswith('macosx'):
             module_extensions = ['.abi3.so', '.so']
-        elif arch.startswith('win'):
-            metadata_arch = 'win'
+        elif platform_tag.startswith('win'):
             module_extensions = ['.pyd']
         else:
-            raise UserException("Unsupported platform tag '{0}'".format(arch))
+            raise UserException(
+                    "Unsupported platform tag '{0}'".format(platform_tag))
 
         package_dir = os.path.dirname(target_qt_dir)
         lgpl = True
@@ -123,9 +121,9 @@ class AbstractPackage(ABC):
                             # '--target-qt-dir' but we still have to handle
                             # older wheels (ie. using versions of Qt released
                             # before '--target-qt-dir' was added.
-                            if metadata_arch == 'linux':
+                            if platform_tag.startswith('manylinux'):
                                 self._fix_linux_rpath(bindings)
-                            elif metadata_arch == 'macos':
+                            elif platform_tag.startswith('macosx'):
                                 self._fix_macos_rpath(bindings)
 
                         break
@@ -140,7 +138,7 @@ class AbstractPackage(ABC):
 
             lgpl = lgpl and metadata.lgpl
 
-            metadata.bundle(name, target_qt_dir, self._qt_dir, metadata_arch,
+            metadata.bundle(name, target_qt_dir, self._qt_dir, platform_tag,
                     self.qt_version, ignore_missing)
 
         return lgpl
@@ -169,6 +167,18 @@ class AbstractPackage(ABC):
         return os.path.join('PyQt{}'.format(qt_major_version),
                 'Qt{}'.format(qt_major_version))
 
+    @staticmethod
+    def missing_executable(exe):
+        """ Return True if an executable cannot be found on PATH. """
+
+        for p in os.environ.get('PATH', '').split(os.pathsep):
+            exe_path = os.path.join(p, exe)
+
+            if os.access(exe_path, os.X_OK):
+                return False
+
+        return True
+
     @property
     def qt_version_str(self):
         """ The version number of the Qt installation as a string. """
@@ -179,7 +189,7 @@ class AbstractPackage(ABC):
     def _fix_linux_rpath(cls, bindings):
         """ Fix the rpath for Linux bindings. """
 
-        if cls._missing_executable('chrpath'):
+        if cls.missing_executable('chrpath'):
             raise UserException("'chrpath' must be installed on your system")
 
         subprocess.run(['chrpath', '--replace', '$ORIGIN/Qt/lib', bindings])
@@ -188,7 +198,7 @@ class AbstractPackage(ABC):
     def _fix_macos_rpath(cls, bindings):
         """ Fix the rpath for macOS bindings. """
 
-        if cls._missing_executable('otool') or cls._missing_executable('install_name_tool'):
+        if cls.missing_executable('otool') or cls.missing_executable('install_name_tool'):
             raise UserException(
                     "'otool' and 'install_name_tool' from Xcode must be "
                     "installed on your system")
@@ -229,18 +239,6 @@ class AbstractPackage(ABC):
             args.append(bindings)
             subprocess.run(args)
 
-    @staticmethod
-    def _missing_executable(exe):
-        """ Return True if an executable cannot be found on PATH. """
-
-        for p in os.environ.get('PATH', '').split(os.pathsep):
-            exe_path = os.path.join(p, exe)
-
-            if os.access(exe_path, os.X_OK):
-                return False
-
-        return True
-
     @staticmethod
     def _parse_version(version_str):
         """ Parse a version string as a 3-tuple of major, minor and maintenance
diff --git a/pyqtbuild/bundle/bundle.py b/pyqtbuild/bundle/bundle.py
index 394e0f9..fe13216 100644
--- a/pyqtbuild/bundle/bundle.py
+++ b/pyqtbuild/bundle/bundle.py
@@ -34,7 +34,7 @@ from .wheel import create_wheel, unpack_wheel, write_record_file
 
 
 def bundle(wheel_path, qt_dir, build_tag_suffix, msvc_runtime, openssl,
-        openssl_dir, exclude, ignore_missing):
+        openssl_dir, exclude, ignore_missing, arch):
     """ Bundle a Qt installation with a PyQt wheel. """
 
     wheel_path = os.path.abspath(wheel_path)
@@ -58,6 +58,16 @@ def bundle(wheel_path, qt_dir, build_tag_suffix, msvc_runtime, openssl,
         raise UserException(
                 "Unable to recognise '{0}' as a wheel name".format(wheel_name))
 
+    # If an architecture is specified then it must be supported by the wheel.
+    if arch:
+        if arch in parts[-1]:
+            pass
+        elif 'universal2' in parts[-1]:
+            parts[-1] = parts[-1].replace('universal2', arch)
+        else:
+            raise UserException(
+                    "'{0}' is not supported by {1}".format(arch, wheel_name))
+
     # Get the package object.
     sub_parts = parts[0].split('_')
     if sub_parts[-1] == 'commercial':
@@ -89,7 +99,7 @@ def bundle(wheel_path, qt_dir, build_tag_suffix, msvc_runtime, openssl,
         if bundled_wheel_dir.endswith(tail):
             bundled_wheel_dir = bundled_wheel_dir[:-len(tail)]
 
-    arch = bundled_wheel_dir.split('-')[-1]
+    platform_tag = bundled_wheel_dir.split('-')[-1]
 
     # Create the directory to contain the existing wheel contents.
     shutil.rmtree(bundled_wheel_dir, ignore_errors=True)
@@ -114,16 +124,16 @@ def bundle(wheel_path, qt_dir, build_tag_suffix, msvc_runtime, openssl,
                         ignore_errors=True)
 
     # Bundle the relevant parts of the Qt installation.
-    package.bundle_qt(target_qt_dir, arch, exclude, ignore_missing)
+    package.bundle_qt(target_qt_dir, platform_tag, exclude, ignore_missing)
 
-    if arch in ('win32', 'win_amd64'):
+    if platform_tag in ('win32', 'win_amd64'):
         # Bundle the MSVC runtime if required.
         if msvc_runtime:
-            package.bundle_msvc_runtime(target_qt_dir, arch)
+            package.bundle_msvc_runtime(target_qt_dir, platform_tag)
 
         # Bundle OpenSSL if required.
         if openssl:
-            package.bundle_openssl(target_qt_dir, openssl_dir, arch)
+            package.bundle_openssl(target_qt_dir, openssl_dir, platform_tag)
 
     # Find the .dist-info directory.
     for distinfo_dir in os.listdir('.'):
diff --git a/pyqtbuild/bundle/bundle_main.py b/pyqtbuild/bundle/bundle_main.py
index ce76646..a3f7739 100644
--- a/pyqtbuild/bundle/bundle_main.py
+++ b/pyqtbuild/bundle/bundle_main.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2020, Riverbank Computing Limited
+# Copyright (c) 2021, Riverbank Computing Limited
 # All rights reserved.
 #
 # This copy of PyQt-builder is licensed for use under the terms of the SIP
@@ -23,6 +23,7 @@
 
 
 from argparse import ArgumentParser
+import sys
 
 from sipbuild import handle_exception
 
@@ -45,6 +46,10 @@ def main():
     parser.add_argument('--verbose', default=False, action='store_true',
             help="enable verbose progress messages")
 
+    if sys.platform == 'darwin':
+        parser.add_argument('--arch', choices=('x86_64', 'arm64'),
+                help="the architecture to bundle")
+
     parser.add_argument('--build-tag-suffix', metavar='SUFFIX',
             help="append SUFFIX to the build tag in the wheel name")
 
@@ -77,11 +82,16 @@ def main():
     try:
         set_verbose(args.verbose)
 
+        try:
+            arch = args.arch
+        except AttributeError:
+            arch = None
+
         bundle(wheel_path=args.wheels[0], qt_dir=args.qt_dir,
                 build_tag_suffix=args.build_tag_suffix,
                 msvc_runtime=args.msvc_runtime, openssl=args.openssl,
                 openssl_dir=args.openssl_dir, exclude=args.exclude,
-                ignore_missing=args.ignore_missing)
+                ignore_missing=args.ignore_missing, arch=arch)
     except Exception as e:
         handle_exception(e)
 
diff --git a/pyqtbuild/bundle/packages/pyqt.py b/pyqtbuild/bundle/packages/pyqt.py
index d5dddcd..7176eaf 100644
--- a/pyqtbuild/bundle/packages/pyqt.py
+++ b/pyqtbuild/bundle/packages/pyqt.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2020, Riverbank Computing Limited
+# Copyright (c) 2021, Riverbank Computing Limited
 # All rights reserved.
 #
 # This copy of PyQt-builder is licensed for use under the terms of the SIP
@@ -36,16 +36,16 @@ _DLLS_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'dlls')
 class PyQt(AbstractPackage):
     """ The base PyQt package. """
 
-    def bundle_msvc_runtime(self, target_qt_dir, arch):
+    def bundle_msvc_runtime(self, target_qt_dir, platform_tag):
         """ Bundle the MSVC runtime. """
 
         verbose("Bundling the MSVC runtime")
 
         self._bundle_dlls(target_qt_dir,
                 os.path.join(_DLLS_DIR,
-                        'msvc-64' if arch == 'win_amd64' else 'msvc-32'))
+                        'msvc-64' if platform_tag == 'win_amd64' else 'msvc-32'))
 
-    def bundle_openssl(self, target_qt_dir, openssl_dir, arch):
+    def bundle_openssl(self, target_qt_dir, openssl_dir, platform_tag):
         """ Bundle the OpenSSL DLLs. """
 
         # Qt v6.2.0 and later include appropriate backends.
@@ -62,7 +62,7 @@ class PyQt(AbstractPackage):
             verbose("Bundling the default OpenSSL libraries")
 
             openssl_dir = os.path.join(_DLLS_DIR,
-                    'openssl-64' if arch == 'win_amd64' else 'openssl-32')
+                    'openssl-64' if platform_tag == 'win_amd64' else 'openssl-32')
 
         self._bundle_dlls(target_qt_dir, openssl_dir)
 
diff --git a/pyqtbuild/bundle/qt_metadata.py b/pyqtbuild/bundle/qt_metadata.py
index 13cf3b3..fe242ce 100644
--- a/pyqtbuild/bundle/qt_metadata.py
+++ b/pyqtbuild/bundle/qt_metadata.py
@@ -59,7 +59,7 @@ class VersionedMetadata:
         self.lgpl = lgpl
         self.legacy = legacy
 
-    def bundle(self, name, target_qt_dir, qt_dir, arch, qt_version,
+    def bundle(self, name, target_qt_dir, qt_dir, platform_tag, qt_version,
             ignore_missing):
         """ Bundle part of Qt as defined by the meta-data. """
 
@@ -68,53 +68,68 @@ class VersionedMetadata:
         if self._name is None:
             self._name = name
 
+        # See if a particular macOS architecture has been specified but only
+        # for versions of Qt that support universal libraries.
+        macos_thin_arch = self._get_macos_thin_arch(platform_tag)
+        if qt_version < (6, 2, 0):
+            macos_thin_arch = None
+
+        if macos_thin_arch is not None:
+            from .abstract_package import AbstractPackage
+
+            if AbstractPackage.missing_executable('lipo'):
+                raise UserException(
+                        "'lipo' from Xcode must be installed on your system")
+
         # Bundle the Qt library that has been wrapped (if there is one).
         if self._dll:
-            self._bundle_qt_library(self._name, target_qt_dir, qt_dir, arch,
-                    qt_version, ignore_missing)
+            self._bundle_qt_library(self._name, target_qt_dir, qt_dir,
+                    qt_version, platform_tag, macos_thin_arch, ignore_missing)
 
         # Bundle any other dependent Qt libraries.
-        for lib_arch, libs in self._lib_deps.items():
-            if lib_arch == '' or lib_arch == arch:
+        for metadata_arch, libs in self._lib_deps.items():
+            if self._is_platform(metadata_arch, platform_tag):
                 for lib in libs:
-                    self._bundle_qt_library(lib, target_qt_dir, qt_dir, arch,
-                            qt_version, ignore_missing)
+                    self._bundle_qt_library(lib, target_qt_dir, qt_dir,
+                            qt_version, platform_tag, macos_thin_arch,
+                            ignore_missing)
 
         # Bundle any other libraries.
         lib_contents = None
 
-        for lib_arch, libs in self._other_lib_deps.items():
-            if lib_arch == '' or lib_arch == arch:
+        for metadata_arch, libs in self._other_lib_deps.items():
+            if self._is_platform(metadata_arch, platform_tag):
                 for lib in libs:
                     if '*' in lib:
                         # A wildcard implies the dependency is optional.  This
                         # is mainly to (historically) deal with ICU on Windows.
                         if lib_contents is None:
                             lib_contents = os.listdir(
-                                    self._get_qt_library_dir(qt_dir, arch))
+                                    self._get_qt_library_dir(qt_dir,
+                                            platform_tag))
 
                         for qt_lib in lib_contents:
                             if fnmatch.fnmatch(qt_lib, lib):
                                 self._bundle_library(qt_lib, target_qt_dir,
-                                        qt_dir, arch, qt_version,
+                                        qt_dir, platform_tag, macos_thin_arch,
                                         ignore_missing)
                     else:
-                        self._bundle_library(lib, target_qt_dir, qt_dir, arch,
-                                qt_version, ignore_missing)
+                        self._bundle_library(lib, target_qt_dir, qt_dir,
+                                platform_tag, macos_thin_arch, ignore_missing)
 
         # Bundle any executables.
-        for exe_arch, exes in self._exes.items():
-            if exe_arch == '' or exe_arch == arch:
+        for metadata_arch, exes in self._exes.items():
+            if self._is_platform(metadata_arch, platform_tag):
                 for exe in exes:
                     bundled_exe = self._bundle_file(exe, target_qt_dir, qt_dir,
-                            arch, qt_version, ignore_missing)
+                            platform_tag, macos_thin_arch, ignore_missing)
 
                     if bundled_exe is not None:
-                        if arch == 'linux':
+                        if self._is_platform('linux', platform_tag):
                             self._fix_linux_executable(bundled_exe, qt_version)
-                        elif arch == 'macos':
+                        elif self._is_platform('macos', platform_tag):
                             self._fix_macos_executable(bundled_exe, qt_version)
-                        elif arch == 'win':
+                        elif self._is_platform('win', platform_tag):
                             self._fix_win_executable(bundled_exe)
 
         # Bundle any QML files.
@@ -125,15 +140,16 @@ class VersionedMetadata:
 
             for qml_subdir in qml_names:
                 self._bundle_nondebug(os.path.join('qml', qml_subdir),
-                        target_qt_dir, qt_dir, arch, qt_version,
+                        target_qt_dir, qt_dir, platform_tag, macos_thin_arch,
                         ignore_missing)
 
         # Bundle any plugins.  We haven't done the analysis of which plugins
         # belong to which package so we assume that only the QtCore package
         # will specify any to exclude and we bundle all of them with that.
         if self._excluded_plugins is not None:
-            self._bundle_nondebug('plugins', target_qt_dir, qt_dir, arch,
-                    qt_version, ignore_missing, exclude=self._excluded_plugins)
+            self._bundle_nondebug('plugins', target_qt_dir, qt_dir,
+                    platform_tag, macos_thin_arch, ignore_missing,
+                    exclude=self._excluded_plugins)
 
         # Bundle any translations:
         if self._translations:
@@ -144,13 +160,14 @@ class VersionedMetadata:
                 if qm.endswith('.qm'):
                     for prefix in self._translations:
                         if qm.startswith(prefix):
-                            self._bundle_file(qm, target_tr_dir, tr_dir, arch,
-                                    qt_version, ignore_missing,
+                            self._bundle_file(qm, target_tr_dir, tr_dir,
+                                    platform_tag, macos_thin_arch,
+                                    ignore_missing,
                                     might_be_code=False)
 
         # Bundle any dynamically created files.
-        for files_arch, files in self._files.items():
-            if files_arch == '' or files_arch == arch:
+        for metadata_arch, files in self._files.items():
+            if self._is_platform(metadata_arch, platform_tag):
                 for fn, content in files:
                     fn = os.path.join(target_qt_dir, fn)
                     os.makedirs(os.path.dirname(fn), exist_ok=True)
@@ -159,11 +176,11 @@ class VersionedMetadata:
                         f.write(content)
 
         # Bundle anything else.
-        for oth_arch, others in self._others.items():
-            if oth_arch == '' or oth_arch == arch:
+        for metadata_arch, others in self._others.items():
+            if self._is_platform(metadata_arch, platform_tag):
                 for oth in others:
                     self._bundle_file(oth, target_qt_dir, qt_dir,
-                            arch, qt_version, ignore_missing,
+                            platform_tag, macos_thin_arch, ignore_missing,
                             might_be_code=False)
 
     def is_applicable(self, qt_version):
@@ -174,8 +191,8 @@ class VersionedMetadata:
         return self._version is None or qt_version >= self._version
 
     @classmethod
-    def _bundle_nondebug(cls, src_dir, target_qt_dir, qt_dir, arch, qt_version,
-            ignore_missing, exclude=None):
+    def _bundle_nondebug(cls, src_dir, target_qt_dir, qt_dir, platform_tag,
+            macos_thin_arch, ignore_missing, exclude=None):
         """ Bundle the non-debug contents of a directory. """
 
         if exclude is None:
@@ -189,20 +206,20 @@ class VersionedMetadata:
                     pass
 
             for name in list(dirnames):
-                if cls._is_debug(name, arch):
+                if cls._is_debug(name, platform_tag):
                     dirnames.remove(name)
 
             for name in filenames:
-                if cls._is_debug(name, arch):
+                if cls._is_debug(name, platform_tag):
                     continue
 
                 cls._bundle_file(
                         os.path.relpath(os.path.join(dirpath, name), qt_dir),
-                        target_qt_dir, qt_dir, arch, qt_version,
+                        target_qt_dir, qt_dir, platform_tag, macos_thin_arch,
                         ignore_missing)
 
     @staticmethod
-    def _bundle_file(name, target_dir, src_dir, arch, qt_version,
+    def _bundle_file(name, target_dir, src_dir, platform_tag, macos_thin_arch,
             ignore_missing, ignore=None, might_be_code=True):
         """ Bundle a file (or directory) and return the name of the installed
         file (or directory) or None if it was missing.
@@ -216,13 +233,13 @@ class VersionedMetadata:
         if os.path.isdir(src):
             shutil.copytree(src, dst, ignore=ignore)
         elif os.path.isfile(src):
-            # See if it worth trying to remove the arm64 code.
-            if arch == 'macos' and might_be_code and qt_version >= (6, 2, 0):
+            if macos_thin_arch is not None and might_be_code:
                 stderr = None if is_verbose() else subprocess.DEVNULL
 
                 try:
                     subprocess.run(
-                            ['lipo', '-thin', 'x86_64', '-output', dst, src],
+                            ['lipo', '-thin', macos_thin_arch, '-output', dst,
+                                    src],
                             stderr=stderr, check=True)
                 except:
                     # If there is any sort of error then just copy it.
@@ -239,27 +256,31 @@ class VersionedMetadata:
         return dst
 
     @classmethod
-    def _bundle_library(cls, name, target_qt_dir, qt_dir, arch, qt_version,
-            ignore_missing, ignore=None):
+    def _bundle_library(cls, name, target_qt_dir, qt_dir, platform_tag,
+            macos_thin_arch, ignore_missing, ignore=None):
         """ Bundle a library. """
 
         cls._bundle_file(name,
-                os.path.join(target_qt_dir, cls._get_qt_library_subdir(arch)),
-                cls._get_qt_library_dir(qt_dir, arch), arch, qt_version,
-                ignore_missing, ignore=ignore)
+                os.path.join(target_qt_dir,
+                        cls._get_qt_library_subdir(platform_tag)),
+                cls._get_qt_library_dir(qt_dir, platform_tag), platform_tag,
+                macos_thin_arch, ignore_missing, ignore=ignore)
 
     @classmethod
-    def _bundle_qt_library(cls, name, target_qt_dir, qt_dir, arch, qt_version,
-            ignore_missing):
+    def _bundle_qt_library(cls, name, target_qt_dir, qt_dir, qt_version,
+            platform_tag, macos_thin_arch, ignore_missing):
         """ Bundle a Qt library. """
 
-        cls._bundle_library(cls._impl_from_library(name, arch, qt_version),
-                target_qt_dir, qt_dir, arch, qt_version, ignore_missing)
+        cls._bundle_library(
+                cls._impl_from_library(name, platform_tag, qt_version),
+                target_qt_dir, qt_dir, platform_tag, macos_thin_arch,
+                ignore_missing)
 
-        if arch == 'macos':
+        if cls._is_platform('macos', platform_tag):
             # Copy the Resources directory without the unnecessary .prl files.
             cls._bundle_library('{}.framework/Resources'.format(name),
-                    target_qt_dir, qt_dir, arch, qt_version, ignore_missing,
+                    target_qt_dir, qt_dir, platform_tag, macos_thin_arch,
+                    ignore_missing,
                     ignore=lambda d, c: [f for f in c if f.endswith('.prl')])
 
     @staticmethod
@@ -323,51 +344,85 @@ class VersionedMetadata:
         cls._create_qt_conf(exe)
 
     @classmethod
-    def _get_qt_library_dir(cls, qt_dir, arch):
+    def _get_macos_thin_arch(cls, platform_tag):
+        """ Return the single macOS architecture, or None if the platform is
+        not macOS or is universal.
+        """
+
+        if cls._is_platform('macos', platform_tag):
+            if platform_tag.endswith('_intel') or platform_tag.endswith('_x86_64'):
+                return 'x86_64'
+
+            if platform_tag.endswith('_arm64'):
+                return 'arm64'
+
+        return None
+
+    @classmethod
+    def _get_qt_library_dir(cls, qt_dir, platform_tag):
         """ Return the name of the directory in the Qt installation containing
         the libraries.
         """
 
-        return os.path.join(qt_dir, cls._get_qt_library_subdir(arch))
+        return os.path.join(qt_dir, cls._get_qt_library_subdir(platform_tag))
 
-    @staticmethod
-    def _get_qt_library_subdir(arch):
+    @classmethod
+    def _get_qt_library_subdir(cls, platform_tag):
         """ Return the name of the sub-directory in the Qt installation
         containing the libraries.
         """
 
-        return 'bin' if arch == 'win' else 'lib'
+        return 'bin' if cls._is_platform('win', platform_tag) else 'lib'
 
-    @staticmethod
-    def _impl_from_library(name, arch, qt_version):
+    @classmethod
+    def _impl_from_library(cls, name, platform_tag, qt_version):
         """ Return the architecture-specific name of a Qt library. """
 
         qt_major = qt_version[0]
 
-        if arch == 'linux':
+        if cls._is_platform('linux', platform_tag):
             return 'libQt{}{}.so.{}'.format(qt_major, name[2:], qt_major)
 
-        if arch == 'macos':
+        if cls._is_platform('macos', platform_tag):
             framework = '5' if qt_major == 5 else 'A'
 
             return '{}.framework/Versions/{}/{}'.format(name, framework, name)
 
-        if arch == 'win':
+        if cls._is_platform('win', platform_tag):
             return 'Qt{}{}.dll'.format(qt_major, name[2:])
 
-    @staticmethod
-    def _is_debug(name, arch):
+    @classmethod
+    def _is_debug(cls, name, platform_tag):
         """ Return True if a name implies a debug version. """
 
-        if arch == 'linux':
+        if cls._is_platform('linux', platform_tag):
             return name.endswith('.debug')
 
-        if arch == 'macos':
+        if cls._is_platform('macos', platform_tag):
             return name.endswith('_debug.dylib') or name.endswith('.dSYM')
 
-        if arch == 'win':
+        if cls._is_platform('win', platform_tag):
             # Special case known non-debug DLLs that end with a 'd'.
             if name.endswith('backend.dll'):
                 return False
 
             return name.endswith('.pdb') or name.endswith('d.dll')
+
+    @staticmethod
+    def _is_platform(metadata_arch, platform_tag):
+        """ Return True if a metadata archtitecture matches a platform tag. """
+
+        # See if it applies to all architectures.
+        if metadata_arch == '':
+            return True
+
+        if metadata_arch == 'linux' and platform_tag.startswith('manylinux'):
+            return True
+
+        if metadata_arch == 'macos' and platform_tag.startswith('macosx'):
+            return True
+
+        if metadata_arch == 'win' and platform_tag.startswith('win'):
+            return True
+
+        return False
diff --git a/pyqtbuild/bundle/qt_wheel.py b/pyqtbuild/bundle/qt_wheel.py
index 8cfbfcb..e93f9ab 100644
--- a/pyqtbuild/bundle/qt_wheel.py
+++ b/pyqtbuild/bundle/qt_wheel.py
@@ -33,7 +33,7 @@ from .wheel import create_wheel, write_record_file
 
 
 def qt_wheel(package, qt_dir, build_tag, suffix, msvc_runtime, openssl,
-        openssl_dir, exclude):
+        openssl_dir, exclude, arch):
     """ Create a wheel containing the subset of a Qt installation required for
     a particular PyQt package.
     """
@@ -61,18 +61,29 @@ def qt_wheel(package, qt_dir, build_tag, suffix, msvc_runtime, openssl,
     # Construct the tag.
     qt_arch = os.path.basename(qt_dir)
     if qt_arch == 'gcc_64':
-        arch = 'manylinux{}_x86_64'.format(
+        platform_tag = 'manylinux{}_x86_64'.format(
                 '_2_28' if package.qt_version[0] == 6 else '2014')
     elif qt_arch in ('macos', 'clang_64'):
-        arch = 'macosx_{}_intel'.format(
-                '10_14' if package.qt_version[0] == 6 else '10_13')
+        if package.qt_version < (6, 2, 0):
+            if arch is not None:
+                raise UserException(
+                        "'--arch' may only be specified for Qt v6.2 and later")
+
+            subarch = 'x86_64'
+        elif arch is None:
+            subarch = 'universal2'
+        else:
+            subarch = arch
+
+        platform_tag = 'macosx_{}_{}'.format(
+                '10_14' if package.qt_version[0] == 6 else '10_13', subarch)
     elif qt_arch.startswith('msvc'):
-        arch = 'win_amd64' if qt_arch.endswith('_64') else 'win32'
+        platform_tag = 'win_amd64' if qt_arch.endswith('_64') else 'win32'
     else:
         raise UserException(
                 "Qt architecture '{0}' is unsupported".format(qt_arch))
 
-    tag_parts = ['py3', 'none', arch]
+    tag_parts = ['py3', 'none', platform_tag]
     tag = '-'.join(tag_parts)
 
     # Construct the name of the wheel.
@@ -98,17 +109,17 @@ def qt_wheel(package, qt_dir, build_tag, suffix, msvc_runtime, openssl,
 
     # Bundle the relevant parts of the Qt installation.
     target_qt_dir = package.get_target_qt_dir()
-    lgpl = package.bundle_qt(target_qt_dir, arch, exclude, ignore_missing=True,
-            bindings=False)
+    lgpl = package.bundle_qt(target_qt_dir, platform_tag, exclude,
+            ignore_missing=True, bindings=False)
 
-    if arch in ('win32', 'win_amd64'):
+    if platform_tag in ('win32', 'win_amd64'):
         # Bundle the MSVC runtime if required.
         if msvc_runtime:
-            package.bundle_msvc_runtime(target_qt_dir, arch)
+            package.bundle_msvc_runtime(target_qt_dir, platform_tag)
 
         # Bundle OpenSSL if required.
         if openssl:
-            package.bundle_openssl(target_qt_dir, openssl_dir, arch)
+            package.bundle_openssl(target_qt_dir, openssl_dir, platform_tag)
 
     # Create the .dist-info directory and populate it from the prototypes.
     os.mkdir(distinfo_dir)
diff --git a/pyqtbuild/bundle/qt_wheel_main.py b/pyqtbuild/bundle/qt_wheel_main.py
index 7e025ee..ae7153b 100644
--- a/pyqtbuild/bundle/qt_wheel_main.py
+++ b/pyqtbuild/bundle/qt_wheel_main.py
@@ -23,6 +23,7 @@
 
 
 from argparse import ArgumentParser
+import sys
 
 from sipbuild import handle_exception
 
@@ -45,6 +46,10 @@ def main():
     parser.add_argument('--verbose', default=False, action='store_true',
             help="enable verbose progress messages")
 
+    if sys.platform == 'darwin':
+        parser.add_argument('--arch', choices=('x86_64', 'arm64'),
+                help="the architecture to create the wheel for")
+
     parser.add_argument('--build-tag', metavar='TAG',
             help="use TAG as the build tag in the wheel name")
 
@@ -77,10 +82,15 @@ def main():
     try:
         set_verbose(args.verbose)
 
+        try:
+            arch = args.arch
+        except AttributeError:
+            arch = None
+
         qt_wheel(package=args.packages[0], qt_dir=args.qt_dir,
                 build_tag=args.build_tag, suffix=args.suffix,
                 msvc_runtime=args.msvc_runtime, openssl=args.openssl,
-                openssl_dir=args.openssl_dir, exclude=args.exclude)
+                openssl_dir=args.openssl_dir, exclude=args.exclude, arch=arch)
     except Exception as e:
         handle_exception(e)
 
diff --git a/pyqtbuild/project.py b/pyqtbuild/project.py
index 6d6cb3a..8a816ad 100644
--- a/pyqtbuild/project.py
+++ b/pyqtbuild/project.py
@@ -129,6 +129,19 @@ class PyQtProject(Project):
         if self.py_pylib_shlib == '':
             self.py_pylib_shlib = pylib_shlib
 
+    def get_platform_tag(self):
+        """ Return the platform tag to use in a wheel name.  This calls the
+        default implementation and replaces 'universal2' with 'x86_64' for
+        versions of Qt that don't support Apple silicon.
+        """
+
+        platform_tag = super().get_platform_tag().split('_')
+
+        if platform_tag[-1] == 'universal2' and self.builder.qt_version < 0x060200:
+            platform_tag[-1] = 'x86_64'
+
+        return '_'.join(platform_tag)
+
     def get_options(self):
         """ Return the list of configurable options. """
 
diff --git a/pyqtbuild/version.py b/pyqtbuild/version.py
index 52f0fa6..af1763e 100644
--- a/pyqtbuild/version.py
+++ b/pyqtbuild/version.py
@@ -1,2 +1,2 @@
-PYQTBUILD_VERSION = 0x010b00
-PYQTBUILD_VERSION_STR = '1.11.0'
+PYQTBUILD_VERSION = 0x010c01
+PYQTBUILD_VERSION_STR = '1.12.1'
diff --git a/setup.py b/setup.py
index 66dcfd9..0e835d9 100644
--- a/setup.py
+++ b/setup.py
@@ -49,7 +49,7 @@ setup(
         version=version,
         license='SIP',
         python_requires='>=3.5',
-        install_requires=['packaging', 'sip >=5.5, <7'],
+        install_requires=['packaging', 'sip >=6.3, <7'],
         packages=find_packages(),
         package_data={
             'pyqtbuild.bundle': ['dlls/*/*', 'qt_wheel_distinfo/*'],



More information about the Neon-commits mailing list