aboutsummaryrefslogtreecommitdiffstats
path: root/build_scripts/main.py
diff options
context:
space:
mode:
Diffstat (limited to 'build_scripts/main.py')
-rw-r--r--build_scripts/main.py458
1 files changed, 248 insertions, 210 deletions
diff --git a/build_scripts/main.py b/build_scripts/main.py
index 806a957e4..9a8d4fb3f 100644
--- a/build_scripts/main.py
+++ b/build_scripts/main.py
@@ -6,14 +6,14 @@ import os
import platform
import re
import sys
+import sysconfig
import time
from packaging.version import parse as parse_version
from pathlib import Path
-from shutil import which, copytree
+from shutil import copytree, rmtree
from textwrap import dedent
# PYSIDE-1760: Pre-load setuptools modules early to avoid racing conditions.
-# Please be careful: All setuptools modules must be loaded before _distutils
# may be touched (should be avoided anyway, btw.)
# Note: This bug is only visible when tools like pyenv are not used. They have some
# pre-loading effect so that setuptools is already in the cache, hiding the problem.
@@ -21,42 +21,40 @@ from setuptools import Command, Extension
from setuptools.command.bdist_egg import bdist_egg as _bdist_egg
from setuptools.command.build_ext import build_ext as _build_ext
from setuptools.command.build_py import build_py as _build_py
+from setuptools.command.build import build as _build
from setuptools.command.develop import develop as _develop
from setuptools.command.install import install as _install
from setuptools.command.install_lib import install_lib as _install_lib
from setuptools.command.install_scripts import install_scripts # noqa: preload only
-# Use the distutils implementation within setuptools (but not before)
-from setuptools._distutils import log
-from setuptools._distutils import sysconfig as sconfig
-from setuptools._distutils.command.build import build as _build
-from setuptools._distutils.errors import DistutilsSetupError
+from .log import log, LogLevel
+from setuptools.errors import SetupError
from .build_info_collector import BuildInfoCollectorMixin
from .config import config
-from .options import OPTION, DistUtilsCommandMixin
+from .options import OPTION, CommandMixin
from .platforms.unix import prepare_packages_posix
from .platforms.windows_desktop import prepare_packages_win32
from .qtinfo import QtInfo
-from .utils import (copydir, copyfile, detect_clang, filter_match,
- get_numpy_location, get_python_dict, init_msvc_env,
+from .utils import (copydir, copyfile, detect_clang,
+ get_numpy_location, get_python_dict,
linux_fix_rpaths_for_library, macos_fix_rpaths_for_library,
platform_cmake_options, remove_tree, run_process,
- run_process_output, update_env_path)
-from .versions import PYSIDE, PYSIDE_MODULE, SHIBOKEN
+ run_process_output, update_env_path, which)
+from . import PYSIDE, PYSIDE_MODULE, SHIBOKEN
from .wheel_override import get_bdist_wheel_override, wheel_module_exists
from .wheel_utils import (get_package_timestamp, get_package_version,
macos_plat_name, macos_pyside_min_deployment_target)
-setup_script_dir = os.getcwd()
-build_scripts_dir = os.path.join(setup_script_dir, 'build_scripts')
-setup_py_path = os.path.join(setup_script_dir, "setup.py")
+setup_script_dir = Path.cwd()
+build_scripts_dir = setup_script_dir / 'build_scripts'
+setup_py_path = setup_script_dir / "setup.py"
-start_time = int(time.time())
+start_time = time.time()
def elapsed():
- return int(time.time()) - start_time
+ return int(time.time() - start_time)
def get_setuptools_extension_modules():
@@ -78,39 +76,35 @@ def _get_make(platform_arch, build_type):
if makespec == "make":
return ("make", "Unix Makefiles")
if makespec == "msvc":
- nmake_path = which("nmake")
- if nmake_path is None or not os.path.exists(nmake_path):
- log.info("nmake not found. Trying to initialize the MSVC env...")
- init_msvc_env(platform_arch, build_type)
- nmake_path = which("nmake")
- if not nmake_path or not os.path.exists(nmake_path):
- raise DistutilsSetupError('"nmake" could not be found.')
if not OPTION["NO_JOM"]:
- jom_path = which("jom")
+ jom_path = Path(which("jom"))
if jom_path:
log.info(f"jom was found in {jom_path}")
return (jom_path, "NMake Makefiles JOM")
+ nmake_path = Path(which("nmake"))
+ if nmake_path is None or not nmake_path.exists():
+ raise SetupError("nmake not found")
log.info(f"nmake was found in {nmake_path}")
if OPTION["JOBS"]:
msg = "Option --jobs can only be used with 'jom' on Windows."
- raise DistutilsSetupError(msg)
+ raise SetupError(msg)
return (nmake_path, "NMake Makefiles")
if makespec == "mingw":
- return ("mingw32-make", "mingw32-make")
+ return (Path("mingw32-make"), "mingw32-make")
if makespec == "ninja":
- return ("ninja", "Ninja")
- raise DistutilsSetupError(f'Invalid option --make-spec "{makespec}".')
+ return (Path("ninja"), "Ninja")
+ raise SetupError(f'Invalid option --make-spec "{makespec}".')
def get_make(platform_arch, build_type):
"""Retrieve the make command and CMake generator name"""
(make_path, make_generator) = _get_make(platform_arch, build_type)
- if not os.path.isabs(make_path):
- found_path = which(make_path)
- if not found_path or not os.path.exists(found_path):
+ if not make_path.is_absolute():
+ found_path = Path(which(make_path))
+ if not found_path or not found_path.exists():
m = (f"You need the program '{make_path}' on your system path to "
f"compile {PYSIDE_MODULE}.")
- raise DistutilsSetupError(m)
+ raise SetupError(m)
make_path = found_path
return (make_path, make_generator)
@@ -161,35 +155,35 @@ def prepare_build():
if install_prefix.endswith("qtbase"):
qt_src_dir = install_prefix
else: # SDK: Use 'Src' directory
- maybe_qt_src_dir = os.path.join(os.path.dirname(install_prefix), 'Src', 'qtbase')
- if os.path.exists(maybe_qt_src_dir):
+ maybe_qt_src_dir = Path(install_prefix).parent / 'Src' / 'qtbase'
+ if maybe_qt_src_dir.exists():
qt_src_dir = maybe_qt_src_dir
-class PysideInstall(_install, DistUtilsCommandMixin):
+class PysideInstall(_install, CommandMixin):
- user_options = _install.user_options + DistUtilsCommandMixin.mixin_user_options
+ user_options = _install.user_options + CommandMixin.mixin_user_options
def __init__(self, *args, **kwargs):
self.command_name = "install"
_install.__init__(self, *args, **kwargs)
- DistUtilsCommandMixin.__init__(self)
+ CommandMixin.__init__(self)
def initialize_options(self):
_install.initialize_options(self)
def finalize_options(self):
- DistUtilsCommandMixin.mixin_finalize_options(self)
+ CommandMixin.mixin_finalize_options(self)
_install.finalize_options(self)
if sys.platform == 'darwin' or self.is_cross_compile:
# Because we change the plat_name to include a correct
- # deployment target on macOS distutils thinks we are
+ # deployment target on macOS setuptools thinks we are
# cross-compiling, and throws an exception when trying to
# execute setup.py install. The check looks like this
# if self.warn_dir and build_plat != get_platform():
- # raise DistutilsPlatformError("Can't install when "
- # "cross-compiling")
+ # raise PlatformError("Can't install when "
+ # "cross-compiling")
# Obviously get_platform will return the old deployment
# target. The fix is to disable the warn_dir flag, which
# was created for bdist_* derived classes to override, for
@@ -241,7 +235,7 @@ class PysideBuildPy(_build_py):
# _install_lib is reimplemented to preserve
-# symlinks when distutils / setuptools copy files to various
+# symlinks when setuptools copy files to various
# directories from the setup tools build dir to the install dir.
class PysideInstallLib(_install_lib):
@@ -255,28 +249,28 @@ class PysideInstallLib(_install_lib):
or into build/wheel when command is 'bdist_wheel'.
"""
- if os.path.isdir(self.build_dir):
+ if self.build_dir.is_dir():
# Using our own copydir makes sure to preserve symlinks.
- outfiles = copydir(os.path.abspath(self.build_dir), os.path.abspath(self.install_dir))
+ outfiles = copydir(Path(self.build_dir).resolve(), Path(self.install_dir).resolve())
else:
self.warn(f"'{self.build_dir}' does not exist -- no Python modules to install")
return
return outfiles
-class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
+class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
- user_options = _build.user_options + DistUtilsCommandMixin.mixin_user_options
+ user_options = _build.user_options + CommandMixin.mixin_user_options
def __init__(self, *args, **kwargs):
self.command_name = "build"
_build.__init__(self, *args, **kwargs)
- DistUtilsCommandMixin.__init__(self)
+ CommandMixin.__init__(self)
BuildInfoCollectorMixin.__init__(self)
def finalize_options(self):
os_name_backup = os.name
- DistUtilsCommandMixin.mixin_finalize_options(self)
+ CommandMixin.mixin_finalize_options(self)
BuildInfoCollectorMixin.collect_and_assign(self)
use_os_name_hack = False
@@ -288,7 +282,7 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
if use_os_name_hack:
# This is a hack to circumvent the dubious check in
- # distutils.commands.build -> finalize_options, which only
+ # setuptool.commands.build -> finalize_options, which only
# allows setting the plat_name for windows NT.
# That is not the case for the wheel module though (which
# does allow setting plat_name), so we circumvent by faking
@@ -351,13 +345,13 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
and parse_version(self.qtinfo.version) >= parse_version("5.7.0")):
clang_dir, clang_source = detect_clang()
if clang_dir:
- clangBinDir = os.path.join(clang_dir, 'bin')
- if clangBinDir not in os.environ.get('PATH'):
+ clangBinDir = clang_dir / 'bin'
+ if str(clangBinDir) not in os.environ.get('PATH'):
log.info(f"Adding {clangBinDir} as detected by {clang_source} to PATH")
additional_paths.append(clangBinDir)
else:
- raise DistutilsSetupError("Failed to detect Clang when checking "
- "LLVM_INSTALL_DIR, CLANG_INSTALL_DIR, llvm-config")
+ raise SetupError("Failed to detect Clang when checking "
+ "LLVM_INSTALL_DIR, CLANG_INSTALL_DIR, llvm-config")
update_env_path(additional_paths)
@@ -368,18 +362,18 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
# Save the shiboken build dir path for clang deployment
# purposes.
- self.shiboken_build_dir = os.path.join(self.build_dir, SHIBOKEN)
+ self.shiboken_build_dir = self.build_dir / SHIBOKEN
self.log_pre_build_info()
# Prepare folders
- if not os.path.exists(self.sources_dir):
+ if not self.sources_dir.exists():
log.info(f"Creating sources folder {self.sources_dir}...")
os.makedirs(self.sources_dir)
- if not os.path.exists(self.build_dir):
+ if not self.build_dir.exists():
log.info(f"Creating build folder {self.build_dir}...")
os.makedirs(self.build_dir)
- if not os.path.exists(self.install_dir):
+ if not self.install_dir.exists():
log.info(f"Creating install folder {self.install_dir}...")
os.makedirs(self.install_dir)
@@ -388,7 +382,7 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
# need to specify a host shiboken path explicitly)
if self.internal_cmake_install_dir_query_file_path:
with open(self.internal_cmake_install_dir_query_file_path, 'w') as f:
- f.write(self.install_dir)
+ f.write(os.fspath(self.install_dir))
if (not OPTION["ONLYPACKAGE"]
and not config.is_internal_shiboken_generator_build_and_part_of_top_level_all()):
@@ -400,10 +394,10 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
# we record the latest successful build and note the
# build directory for supporting the tests.
timestamp = time.strftime('%Y-%m-%d_%H%M%S')
- build_history = os.path.join(setup_script_dir, 'build_history')
- unique_dir = os.path.join(build_history, timestamp)
- os.makedirs(unique_dir)
- fpath = os.path.join(unique_dir, 'build_dir.txt')
+ build_history = setup_script_dir / 'build_history'
+ unique_dir = build_history / timestamp
+ unique_dir.mkdir(parents=True)
+ fpath = unique_dir / 'build_dir.txt'
with open(fpath, 'w') as f:
print(self.build_dir, file=f)
print(self.build_classifiers, file=f)
@@ -437,14 +431,14 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
if _project is not None:
if not _wheel_path.exists():
- _wheel_path.mkdir()
+ _wheel_path.mkdir(parents=True)
_src = Path(_path / _project)
_dst = Path(_wheel_path / _project)
# Remove the directory in case it exists.
# This applies to 'shiboken6', 'shiboken6_generator',
# and 'pyside6' inside the 'package_for_wheels' directory.
if _dst.exists():
- log.warn(f'***** Found directory "{_dst}", removing it first.')
+ log.warning(f'Found directory "{_dst}", removing it first.')
remove_tree(_dst)
try:
@@ -452,8 +446,8 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
# is used when using the 'install' setup.py instruction.
copytree(_src, _dst)
except Exception as e:
- log.warn(f'***** problem renaming "{self.st_build_dir}"')
- log.warn(f'ignored error: {type(e).__name__}: {e}')
+ log.warning(f'problem renaming "{self.st_build_dir}"')
+ log.warning(f'ignored error: {type(e).__name__}: {e}')
else:
log.info("Skipped preparing and building packages.")
log.info(f"--- Build completed ({elapsed()}s)")
@@ -462,7 +456,7 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
if config.is_internal_shiboken_generator_build_and_part_of_top_level_all():
return
- setuptools_install_prefix = sconfig.get_python_lib(1)
+ setuptools_install_prefix = sysconfig.get_paths()["purelib"]
if OPTION["FINAL_INSTALL_PREFIX"]:
setuptools_install_prefix = OPTION["FINAL_INSTALL_PREFIX"]
log.info("=" * 30)
@@ -524,16 +518,13 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
return
self._patchelf_path = which('patchelf')
if self._patchelf_path:
- if not os.path.isabs(self._patchelf_path):
- self._patchelf_path = os.path.join(os.getcwd(), self._patchelf_path)
+ self._patchelf_path = Path(self._patchelf_path)
+ if not self._patchelf_path.is_absolute():
+ self._patchelf_path = Path.cwd() / self._patchelf_path
log.info(f"Using {self._patchelf_path} ...")
return
- log.info("Building patchelf...")
- module_src_dir = os.path.join(self.sources_dir, "patchelf")
- build_cmd = ["g++", f"{module_src_dir}/patchelf.cc", "-o", "patchelf"]
- if run_process(build_cmd) != 0:
- raise DistutilsSetupError("Error building patchelf")
- self._patchelf_path = os.path.join(self.script_dir, "patchelf")
+ else:
+ raise SetupError("patchelf not found")
def _enable_numpy(self):
if OPTION["ENABLE_NUMPY_SUPPORT"] or OPTION["PYSIDE_NUMPY_SUPPORT"]:
@@ -553,13 +544,13 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
# Prepare folders
os.chdir(self.build_dir)
- module_build_dir = os.path.join(self.build_dir, extension)
- skipflag_file = f"{module_build_dir} -skip"
- if os.path.exists(skipflag_file):
+ module_build_dir = self.build_dir / extension
+ skipflag_file = Path(f"{module_build_dir}-skip")
+ if skipflag_file.exists():
log.info(f"Skipping {extension} because {skipflag_file} exists")
return
- module_build_exists = os.path.exists(module_build_dir)
+ module_build_exists = module_build_dir.exists()
if module_build_exists:
if not OPTION["REUSE_BUILD"]:
log.info(f"Deleting module build folder {module_build_dir}...")
@@ -570,23 +561,31 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
log.error(f'ignored error: {e}')
else:
log.info(f"Reusing module build folder {module_build_dir}...")
- if not os.path.exists(module_build_dir):
+ if not module_build_dir.exists():
log.info(f"Creating module build folder {module_build_dir}...")
os.makedirs(module_build_dir)
os.chdir(module_build_dir)
- module_src_dir = os.path.join(self.sources_dir, extension)
+ module_src_dir = self.sources_dir / extension
# Build module
- cmake_cmd = [OPTION["CMAKE"]]
- if OPTION["QUIET"]:
+ cmake_cmd = [str(OPTION["CMAKE"])]
+ cmake_quiet_build = 1
+ cmake_rule_messages = 0
+ if OPTION["LOG_LEVEL"] == LogLevel.VERBOSE:
# Pass a special custom option, to allow printing a lot less information when doing
# a quiet build.
- cmake_cmd.append('-DQUIET_BUILD=1')
+ cmake_quiet_build = 0
if self.make_generator == "Unix Makefiles":
# Hide progress messages for each built source file.
# Doesn't seem to work if set within the cmake files themselves.
- cmake_cmd.append('-DCMAKE_RULE_MESSAGES=0')
+ cmake_rule_messages = 1
+
+ if OPTION["UNITY"]:
+ cmake_cmd.append("-DCMAKE_UNITY_BUILD=ON")
+ batch_size = OPTION["UNITY_BUILD_BATCH_SIZE"]
+ cmake_cmd.append(f"-DCMAKE_UNITY_BUILD_BATCH_SIZE={batch_size}")
+ log.info("Using UNITY build")
cmake_cmd += [
"-G", self.make_generator,
@@ -597,7 +596,9 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
# Record the minimum/maximum Python version for later use in Shiboken.__init__
f"-DMINIMUM_PYTHON_VERSION={get_allowed_python_versions()[0]}",
f"-DMAXIMUM_PYTHON_VERSION={get_allowed_python_versions()[-1]}",
- module_src_dir
+ f"-DQUIET_BUILD={cmake_quiet_build}",
+ f"-DCMAKE_RULE_MESSAGES={cmake_rule_messages}",
+ str(module_src_dir)
]
# When cross-compiling we set Python_ROOT_DIR to tell
@@ -610,14 +611,14 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
# embedding_generator.py. Pass it as a separate option.
cmake_cmd.append(f"-DQFP_PYTHON_HOST_PATH={sys.executable}")
else:
- cmake_cmd.append(f"-DPYTHON_EXECUTABLE={self.py_executable}")
- cmake_cmd.append(f"-DPYTHON_INCLUDE_DIR={self.py_include_dir}")
- cmake_cmd.append(f"-DPYTHON_LIBRARY={self.py_library}")
+ cmake_cmd.append(f"-DPython_EXECUTABLE={self.py_executable}")
+ cmake_cmd.append(f"-DPython_INCLUDE_DIR={self.py_include_dir}")
+ cmake_cmd.append(f"-DPython_LIBRARY={self.py_library}")
# If a custom shiboken cmake config directory path was provided, pass it to CMake.
if OPTION["SHIBOKEN_CONFIG_DIR"] and config.is_internal_pyside_build():
config_dir = OPTION["SHIBOKEN_CONFIG_DIR"]
- if os.path.exists(config_dir):
+ if config_dir.exists():
log.info(f"Using custom provided {SHIBOKEN} installation: {config_dir}")
cmake_cmd.append(f"-DShiboken6_DIR={config_dir}")
else:
@@ -634,6 +635,7 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
module_sub_set += ';'
module_sub_set += m
cmake_cmd.append(f"-DMODULES={module_sub_set}")
+
if OPTION["SKIP_MODULES"]:
skip_modules = ''
for m in OPTION["SKIP_MODULES"].split(','):
@@ -670,12 +672,9 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
if numpy:
cmake_cmd.append(f"-DNUMPY_INCLUDE_DIR={numpy}")
else:
- log.warn('***** numpy include directory was not found.')
+ log.warning('numpy include directory was not found.')
- if self.build_type.lower() == 'debug':
- if not self.is_cross_compile:
- cmake_cmd.append(f"-DPYTHON_DEBUG_LIBRARY={self.py_library}")
- else:
+ if self.build_type.lower() != 'debug':
if OPTION['NO_STRIP']:
cmake_cmd.append("-DQFP_NO_STRIP=1")
if OPTION['NO_OVERRIDE_OPTIMIZATION_FLAGS']:
@@ -686,13 +685,20 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
elif OPTION["LIMITED_API"] == "no":
cmake_cmd.append("-DFORCE_LIMITED_API=no")
elif not OPTION["LIMITED_API"]:
- pass
+ if sys.platform == 'win32' and self.debug:
+ cmake_cmd.append("-DFORCE_LIMITED_API=no")
else:
- raise DistutilsSetupError("option limited-api must be 'yes' or 'no' "
- "(default yes if applicable, i.e. python version >= 3.6)")
+ raise SetupError("option limited-api must be 'yes' or 'no' "
+ "(default yes if applicable, i.e. Python "
+ "version >= 3.9 and release build if on Windows)")
+
+ if OPTION["DISABLE_PYI"]:
+ cmake_cmd.append("-DDISABLE_PYI=yes")
- if OPTION["VERBOSE_BUILD"]:
+ if OPTION["LOG_LEVEL"] == LogLevel.VERBOSE:
cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON")
+ else:
+ cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=OFF")
if OPTION['COMPILER_LAUNCHER']:
compiler_launcher = OPTION['COMPILER_LAUNCHER']
@@ -705,7 +711,7 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
or sys.platform.startswith('darwin')):
cmake_cmd.append("-DSANITIZE_ADDRESS=ON")
else:
- raise DistutilsSetupError("Address sanitizer can only be used on Linux and macOS.")
+ raise SetupError("Address sanitizer can only be used on Linux and macOS.")
if extension.lower() == PYSIDE:
pyside_qt_conf_prefix = ''
@@ -718,6 +724,9 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
pyside_qt_conf_prefix = '"."'
cmake_cmd.append(f"-DPYSIDE_QT_CONF_PREFIX={pyside_qt_conf_prefix}")
+ if OPTION["STANDALONE"]:
+ cmake_cmd.append("-DSTANDALONE:BOOL=ON")
+
# Pass package version to CMake, so this string can be
# embedded into _config.py file.
package_version = get_package_version()
@@ -732,8 +741,7 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
cmake_cmd.append(f"-DPACKAGE_SETUP_PY_PACKAGE_TIMESTAMP={timestamp}")
if extension.lower() in [SHIBOKEN]:
- cmake_cmd.append("-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=yes")
- cmake_cmd.append("-DUSE_PYTHON_VERSION=3.6")
+ cmake_cmd.append("-DUSE_PYTHON_VERSION=3.9")
cmake_cmd += platform_cmake_options()
@@ -764,7 +772,7 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
# Set macOS minimum deployment target (version).
# This is required so that calling
- # run_process -> distutils.spawn()
+ # run_process -> subprocess.call()
# does not set its own minimum deployment target
# environment variable which is based on the python
# interpreter sysconfig value.
@@ -775,7 +783,7 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
os.environ['MACOSX_DEPLOYMENT_TARGET'] = deployment_target
if OPTION["BUILD_DOCS"]:
- # Build the whole documentation (rst + API) by default
+ # Build the whole documentation (Base + API) by default
cmake_cmd.append("-DFULLDOCSBUILD=1")
if OPTION["DOC_BUILD_ONLINE"]:
@@ -801,9 +809,9 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
cmake_cmd.append(f"-DQFP_QT_HOST_PATH={self.qt_host_path}")
if self.is_cross_compile and (not OPTION["SHIBOKEN_HOST_PATH"]
- or not os.path.exists(OPTION["SHIBOKEN_HOST_PATH"])):
- raise DistutilsSetupError(
- "Please specify the location of host shiboken tools via --shiboken-host-path=")
+ or not Path(OPTION["SHIBOKEN_HOST_PATH"]).exists()):
+ raise SetupError("Please specify the location of host shiboken tools via "
+ "--shiboken-host-path=")
if self.shiboken_host_path:
cmake_cmd.append(f"-DQFP_SHIBOKEN_HOST_PATH={self.shiboken_host_path}")
@@ -821,18 +829,18 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
if not OPTION["SKIP_CMAKE"]:
log.info(f"Configuring module {extension} ({module_src_dir})...")
if run_process(cmake_cmd) != 0:
- raise DistutilsSetupError(f"Error configuring {extension}")
+ raise SetupError(f"Error configuring {extension}")
else:
log.info(f"Reusing old configuration for module {extension} ({module_src_dir})...")
log.info(f"-- Compiling module {extension}...")
- cmd_make = [self.make_path]
+ cmd_make = [str(self.make_path)]
if OPTION["JOBS"]:
cmd_make.append(OPTION["JOBS"])
- if OPTION["VERBOSE_BUILD"] and self.make_generator == "Ninja":
+ if OPTION["LOG_LEVEL"] == LogLevel.VERBOSE and self.make_generator == "Ninja":
cmd_make.append("-v")
if run_process(cmd_make) != 0:
- raise DistutilsSetupError(f"Error compiling {extension}")
+ raise SetupError(f"Error compiling {extension}")
if sys.version_info == (3, 6) and sys.platform == "darwin":
# Python 3.6 has a Sphinx problem because of docutils 0.17 .
@@ -846,12 +854,11 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
found = importlib.util.find_spec("sphinx")
if found:
log.info("Generating Shiboken documentation")
- make_doc_cmd = [self.make_path, "doc"]
- if OPTION["VERBOSE_BUILD"] and self.make_generator == "Ninja":
+ make_doc_cmd = [str(self.make_path), "doc"]
+ if OPTION["LOG_LEVEL"] == LogLevel.VERBOSE and self.make_generator == "Ninja":
make_doc_cmd.append("-v")
if run_process(make_doc_cmd) != 0:
- raise DistutilsSetupError("Error generating documentation "
- f"for {extension}")
+ raise SetupError(f"Error generating documentation for {extension}")
else:
log.info("Sphinx not found, skipping documentation build")
else:
@@ -869,8 +876,8 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
time.sleep(1)
# ninja: error: unknown target 'install/fast'
target = 'install/fast' if self.make_generator != 'Ninja' else 'install'
- if run_process([self.make_path, target]) != 0:
- raise DistutilsSetupError(f"Error pseudo installing {extension}")
+ if run_process([str(self.make_path), target]) != 0:
+ raise SetupError(f"Error pseudo installing {extension}")
else:
log.info(f"Skipped installing module {extension}")
@@ -879,13 +886,13 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
def prepare_packages(self):
"""
This will copy all relevant files from the various locations in the "cmake install dir",
- to the setup tools build dir (which is read from self.build_lib provided by distutils).
+ to the setup tools build dir (which is read from self.build_lib provided by setuptools).
After that setuptools.command.build_py is smart enough to copy everything
from the build dir to the install dir (the virtualenv site-packages for example).
"""
try:
- log.info("\nPreparing setup tools build directory.\n")
+ log.info("Preparing setup tools build directory.")
_vars = {
"site_packages_dir": self.site_packages_dir,
"sources_dir": self.sources_dir,
@@ -902,6 +909,8 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
"qt_data_dir": self.qtinfo.data_dir,
"qt_doc_dir": self.qtinfo.docs_dir,
"qt_lib_dir": self.qtinfo.libs_dir,
+ "qt_module_json_files_dir": self.qtinfo.module_json_files_dir,
+ "qt_metatypes_dir": self.qtinfo.metatypes_dir,
"qt_lib_execs_dir": self.qtinfo.lib_execs_dir,
"qt_plugins_dir": self.qtinfo.plugins_dir,
"qt_prefix_dir": self.qtinfo.prefix_dir,
@@ -927,33 +936,31 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
# when copying the files for each of the sub-projects and
# we don't want to accidentally install shiboken files
# as part of pyside-tools package.
- if os.path.isdir(self.st_build_dir):
+ if self.st_build_dir.is_dir():
log.info(f"Removing {self.st_build_dir}")
try:
remove_tree(self.st_build_dir)
except Exception as e:
- log.warn(f'***** problem removing "{self.st_build_dir}"')
- log.warn(f'ignored error: {e}')
+ log.warning(f'problem removing "{self.st_build_dir}"')
+ log.warning(f'ignored error: {e}')
if sys.platform == "win32":
_vars['dbg_postfix'] = OPTION["DEBUG"] and "_d" or ""
return prepare_packages_win32(self, _vars)
else:
- return prepare_packages_posix(self, _vars)
+ return prepare_packages_posix(self, _vars, self.is_cross_compile)
except IOError as e:
print('setup.py/prepare_packages: ', e)
raise
def qt_is_framework_build(self):
- if os.path.isdir(f"{self.qtinfo.headers_dir}/../lib/QtCore.framework"):
- return True
- return False
+ return Path(f"{self.qtinfo.headers_dir}/../lib/QtCore.framework").is_dir()
def get_built_pyside_config(self, _vars):
# Get config that contains list of built modules, and
# SOVERSIONs of the built libraries.
- st_build_dir = _vars['st_build_dir']
- config_path = os.path.join(st_build_dir, config.package_name(), "_config.py")
+ st_build_dir = Path(_vars['st_build_dir'])
+ config_path = st_build_dir / config.package_name() / "_config.py"
temp_config = get_python_dict(config_path)
if 'built_modules' not in temp_config:
temp_config['built_modules'] = []
@@ -971,11 +978,11 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
"""
log.info('Finding path to the libclang shared library.')
cmake_cmd = [
- OPTION["CMAKE"],
+ str(OPTION["CMAKE"]),
"-L", # Lists variables
"-N", # Just inspects the cache (faster)
"-B", # Specifies the build dir
- self.shiboken_build_dir
+ str(self.shiboken_build_dir)
]
out = run_process_output(cmake_cmd)
lines = [s.strip() for s in out]
@@ -996,10 +1003,11 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
# clang_lib_path points to the static import library
# (lib/libclang.lib), whereas we want to copy the shared
# library (bin/libclang.dll).
- clang_lib_path = re.sub(r'lib/libclang.lib$',
- 'bin/libclang.dll',
- clang_lib_path)
+ clang_lib_path = Path(re.sub(r'lib/libclang.lib$',
+ 'bin/libclang.dll',
+ clang_lib_path))
else:
+ clang_lib_path = Path(clang_lib_path)
# shiboken6 links against libclang.so.6 or a similarly
# named library.
# If the linked against library is a symlink, resolve
@@ -1011,26 +1019,26 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
# E.g. On Linux libclang.so -> libclang.so.6 ->
# libclang.so.6.0.
# "libclang.so.6" is the name we want for the copied file.
- if os.path.islink(clang_lib_path):
- link_target = os.readlink(clang_lib_path)
- if os.path.isabs(link_target):
+ if clang_lib_path.is_symlink():
+ link_target = Path(os.readlink(clang_lib_path))
+ if link_target.is_absolute():
clang_lib_path = link_target
else:
# link_target is relative, transform to absolute.
- clang_lib_path = os.path.join(os.path.dirname(clang_lib_path), link_target)
- clang_lib_path = os.path.abspath(clang_lib_path)
+ clang_lib_path = clang_lib_path.parent / link_target
+ clang_lib_path = clang_lib_path.resolve()
# The destination will be the shiboken package folder.
_vars = {}
_vars['st_build_dir'] = self.st_build_dir
_vars['st_package_name'] = config.package_name()
- destination_dir = "{st_build_dir}/{st_package_name}".format(**_vars)
+ destination_dir = Path("{st_build_dir}/{st_package_name}".format(**_vars))
- if os.path.exists(clang_lib_path):
- basename = os.path.basename(clang_lib_path)
+ if clang_lib_path.exists():
+ basename = clang_lib_path.name
log.info(f"Copying libclang shared library {clang_lib_path} to the package "
f"folder as {basename}.")
- destination_path = os.path.join(destination_dir, basename)
+ destination_path = destination_dir / basename
# Need to modify permissions in case file is not writable
# (a reinstall would cause a permission denied error).
@@ -1049,11 +1057,11 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
if self.is_cross_compile:
if 'darwin' in self.plat_name or 'macos' in self.plat_name:
filters = darwin_filters
- elif 'linux' in self.plat_name:
+ elif 'linux' in self.plat_name or 'android' in self.plat_name:
filters = unix_filters
else:
- log.warn(f"No shared library filters found for platform {self.plat_name}. "
- f"The package might miss Qt libraries and plugins.")
+ log.warning(f"No shared library filters found for platform {self.plat_name}. "
+ f"The package might miss Qt libraries and plugins.")
else:
if sys.platform == 'darwin':
filters = darwin_filters
@@ -1061,29 +1069,31 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
filters = unix_filters
return filters
+ def _find_shared_libraries(self, path, recursive=False):
+ """Helper to find shared libraries in a path."""
+ result = set()
+ for filter in self.get_shared_library_filters():
+ glob_pattern = f"**/{filter}" if recursive else filter
+ for library in path.glob(glob_pattern):
+ result.add(library)
+ return list(result)
+
def package_libraries(self, package_path):
"""Returns the libraries of the Python module"""
- filters = self.get_shared_library_filters()
- return [lib for lib in os.listdir(
- package_path) if filter_match(lib, filters)]
+ return self._find_shared_libraries(package_path)
def get_shared_libraries_in_path_recursively(self, initial_path):
"""Returns shared library plugins in given path (collected
recursively)"""
- filters = self.get_shared_library_filters()
- libraries = []
- for dir_path, dir_names, file_names in os.walk(initial_path):
- for name in file_names:
- if filter_match(name, filters):
- library_path = os.path.join(dir_path, name)
- libraries.append(library_path)
- return libraries
-
- def update_rpath(self, package_path, executables, libexec=False):
+ return self._find_shared_libraries(initial_path, recursive=True)
+
+ def update_rpath(self, executables, libexec=False, message=None):
ROOT = '@loader_path' if sys.platform == 'darwin' else '$ORIGIN'
QT_PATH = '/../lib' if libexec else '/Qt/lib'
+ message = "Patched rpath to '$ORIGIN/' in"
if sys.platform.startswith('linux'):
+
def rpath_cmd(srcpath):
final_rpath = ''
# Command line rpath option takes precedence over
@@ -1101,6 +1111,8 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
override=override)
elif sys.platform == 'darwin':
+ message = "Updated rpath in"
+
def rpath_cmd(srcpath):
final_rpath = ''
# Command line rpath option takes precedence over
@@ -1118,21 +1130,20 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
raise RuntimeError(f"Not configured for platform {sys.platform}")
# Update rpath
- for srcname in executables:
- srcpath = os.path.join(package_path, srcname)
- if os.path.isdir(srcpath) or os.path.islink(srcpath):
+ for executable in executables:
+ if executable.is_dir() or executable.is_symlink():
continue
- if not os.path.exists(srcpath):
+ if not executable.exists():
continue
- rpath_cmd(srcpath)
- log.info("Patched rpath to '$ORIGIN/' (Linux) or "
- f"updated rpath (OS/X) in {srcpath}.")
+ rpath_cmd(executable)
+ log.debug(f"{message} {executable}.")
def update_rpath_for_linux_plugins(
self,
plugin_paths,
qt_lib_dir=None,
is_qml_plugin=False):
+
# If the linux sysroot (where the plugins are copied from)
# is from a mainline distribution, it might have a different
# directory layout than then one we expect to have in the
@@ -1145,21 +1156,24 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
log.info("Patching rpath for Qt and QML plugins.")
for plugin in plugin_paths:
- if os.path.isdir(plugin) or os.path.islink(plugin):
+ if plugin.is_dir() or plugin.is_symlink():
continue
- if not os.path.exists(plugin):
+ if not plugin.exists():
continue
if is_qml_plugin:
- plugin_dir = os.path.dirname(plugin)
+ plugin_dir = plugin.parent
+ # FIXME: there is no os.path.relpath equivalent on pathlib.
+ # The Path.relative_to is not equivalent and raises ValueError when the paths
+ # are not subpaths, so it doesn't generate "../../something".
rel_path_from_qml_plugin_qt_lib_dir = os.path.relpath(qt_lib_dir, plugin_dir)
- rpath_value = os.path.join("$ORIGIN", rel_path_from_qml_plugin_qt_lib_dir)
+ rpath_value = Path("$ORIGIN") / rel_path_from_qml_plugin_qt_lib_dir
else:
rpath_value = "$ORIGIN/../../lib"
linux_fix_rpaths_for_library(self._patchelf_path, plugin, rpath_value,
override=True)
- log.info(f"Patched rpath to '{rpath_value}' in {plugin}.")
+ log.debug(f"Patched rpath to '{rpath_value}' in {plugin}.")
def update_rpath_for_linux_qt_libraries(self, qt_lib_dir):
# Ensure that Qt libs and ICU libs have $ORIGIN in their rpath.
@@ -1168,101 +1182,125 @@ class PysideBuild(_build, DistUtilsCommandMixin, BuildInfoCollectorMixin):
if not (self.is_cross_compile and sys.platform.startswith('linux') and self.standalone):
return
+ qt_lib_dir = Path(qt_lib_dir)
rpath_value = "$ORIGIN"
log.info(f"Patching rpath for Qt and ICU libraries in {qt_lib_dir}.")
- libs = self.package_libraries(qt_lib_dir)
- lib_paths = [os.path.join(qt_lib_dir, lib) for lib in libs]
- for library in lib_paths:
- if os.path.isdir(library) or os.path.islink(library):
+ for library in self.package_libraries(qt_lib_dir):
+ if library.is_dir() or library.is_symlink():
continue
- if not os.path.exists(library):
+ if not library.exists():
continue
linux_fix_rpaths_for_library(self._patchelf_path, library, rpath_value, override=True)
- log.info(f"Patched rpath to '{rpath_value}' in {library}.")
+ log.debug(f"Patched rpath to '{rpath_value}' in {library}.")
-class PysideRstDocs(Command, DistUtilsCommandMixin):
- description = "Build .rst documentation only"
- user_options = DistUtilsCommandMixin.mixin_user_options
+class PysideBaseDocs(Command, CommandMixin):
+ description = "Build the base documentation only"
+ user_options = CommandMixin.mixin_user_options
def __init__(self, *args, **kwargs):
- self.command_name = "build_rst_docs"
+ self.command_name = "build_base_docs"
Command.__init__(self, *args, **kwargs)
- DistUtilsCommandMixin.__init__(self)
+ CommandMixin.__init__(self)
def initialize_options(self):
- log.info("-- This build process will not include the API documentation."
+ log.info("-- This build process will not include the API documentation. "
"API documentation requires a full build of pyside/shiboken.")
self.skip = False
if config.is_internal_shiboken_generator_build():
self.skip = True
if not self.skip:
self.name = config.package_name().lower()
- self.doc_dir = os.path.join(config.setup_script_dir, "sources")
- self.doc_dir = os.path.join(self.doc_dir, self.name)
- self.doc_dir = os.path.join(self.doc_dir, "doc")
+ self.doc_dir = config.setup_script_dir / "sources" / self.name / "doc"
# Check if sphinx is installed to proceed.
found = importlib.util.find_spec("sphinx")
+ self.html_dir = Path("html")
if found:
if self.name == SHIBOKEN:
+ # Delete the 'html' directory since new docs will be generated anyway
+ if self.html_dir.is_dir():
+ rmtree(self.html_dir)
+ log.info("-- Deleted old html directory")
log.info("-- Generating Shiboken documentation")
log.info(f"-- Documentation directory: 'html/{PYSIDE}/{SHIBOKEN}/'")
elif self.name == PYSIDE:
log.info("-- Generating PySide documentation")
log.info(f"-- Documentation directory: 'html/{PYSIDE}/'")
else:
- raise DistutilsSetupError("Sphinx not found - aborting")
- self.html_dir = "html"
+ raise SetupError("Sphinx not found - aborting")
# creating directories html/pyside6/shiboken6
try:
- if not os.path.isdir(self.html_dir):
- os.mkdir(self.html_dir)
+ if not self.html_dir.is_dir():
+ self.html_dir.mkdir(parents=True)
if self.name == SHIBOKEN:
- out_pyside = os.path.join(self.html_dir, PYSIDE)
- if not os.path.isdir(out_pyside):
- os.mkdir(out_pyside)
- out_shiboken = os.path.join(out_pyside, SHIBOKEN)
- if not os.path.isdir(out_shiboken):
- os.mkdir(out_shiboken)
+ out_pyside = self.html_dir / PYSIDE
+ if not out_pyside.is_dir():
+ out_pyside.mkdir(parents=True)
+ out_shiboken = out_pyside / SHIBOKEN
+ if not out_shiboken.is_dir():
+ out_shiboken.mkdir(parents=True)
self.out_dir = out_shiboken
# We know that on the shiboken step, we already created the
# 'pyside6' directory
elif self.name == PYSIDE:
- self.out_dir = os.path.join(self.html_dir, PYSIDE)
+ self.out_dir = self.html_dir / PYSIDE
except (PermissionError, FileExistsError):
- raise DistutilsSetupError(f"Error while creating directories for {self.doc_dir}")
+ raise SetupError(f"Error while creating directories for {self.doc_dir}")
def run(self):
if not self.skip:
cmake_cmd = [
- OPTION["CMAKE"],
- "-S", self.doc_dir,
- "-B", self.out_dir,
+ str(OPTION["CMAKE"]),
+ "-S", str(self.doc_dir),
+ "-B", str(self.out_dir),
"-DDOC_OUTPUT_FORMAT=html",
"-DFULLDOCSBUILD=0",
]
- if OPTION["QUIET"]:
- cmake_cmd.append('-DQUIET_BUILD=1')
+
+ cmake_quiet_build = 1
+ cmake_message_log_level = "STATUS"
+
+ # Define log level
+ if OPTION["LOG_LEVEL"] == LogLevel.VERBOSE:
+ cmake_quiet_build = 0
+ cmake_message_log_level = "VERBOSE"
+ elif OPTION["LOG_LEVEL"] == LogLevel.QUIET:
+ cmake_message_log_level = "ERROR"
+
+ cmake_cmd.append(f"-DQUIET_BUILD={cmake_quiet_build}")
+ cmake_cmd.append(f"-DCMAKE_MESSAGE_LOG_LEVEL={cmake_message_log_level}")
+
if run_process(cmake_cmd) != 0:
- raise DistutilsSetupError(f"Error running CMake for {self.doc_dir}")
+ raise SetupError(f"Error running CMake for {self.doc_dir}")
if self.name == PYSIDE:
- self.sphinx_src = os.path.join(self.out_dir, "rst")
+ self.sphinx_src = self.out_dir / "base"
+ example_gallery = config.setup_script_dir / "tools" / "example_gallery" / "main.py"
+ assert example_gallery.is_file()
+ example_gallery_cmd = [sys.executable, os.fspath(example_gallery)]
+ if OPTION["LOG_LEVEL"] == LogLevel.QUIET:
+ example_gallery_cmd.append("--quiet")
+ qt_src_dir = OPTION['QT_SRC']
+ if qt_src_dir:
+ example_gallery_cmd.extend(["--qt-src-dir", qt_src_dir])
+ if run_process(example_gallery_cmd) != 0:
+ raise SetupError(f"Error running example gallery for {self.doc_dir}")
elif self.name == SHIBOKEN:
self.sphinx_src = self.out_dir
- sphinx_cmd = ["sphinx-build", "-b", "html", "-c", self.sphinx_src,
- self.doc_dir, self.out_dir]
+ sphinx_cmd = ["sphinx-build", "-b", "html", "-j", "auto", "-c",
+ str(self.sphinx_src), str(self.doc_dir),
+ str(self.out_dir)]
if run_process(sphinx_cmd) != 0:
- raise DistutilsSetupError(f"Error running CMake for {self.doc_dir}")
+ raise SetupError(f"Error running CMake for {self.doc_dir}")
# Last message
if not self.skip and self.name == PYSIDE:
log.info(f"-- The documentation was built. Check html/{PYSIDE}/index.html")
def finalize_options(self):
- DistUtilsCommandMixin.mixin_finalize_options(self)
+ CommandMixin.mixin_finalize_options(self)
cmd_class_dict = {
@@ -1273,7 +1311,7 @@ cmd_class_dict = {
'develop': PysideDevelop,
'install': PysideInstall,
'install_lib': PysideInstallLib,
- 'build_rst_docs': PysideRstDocs,
+ 'build_base_docs': PysideBaseDocs,
}
if wheel_module_exists:
pyside_bdist_wheel = get_bdist_wheel_override()