diff options
-rw-r--r-- | qtinfo.py | 145 | ||||
-rw-r--r-- | setup.py | 561 | ||||
-rw-r--r-- | sources/pyside2/CMakeLists.txt | 83 | ||||
-rw-r--r-- | sources/pyside2/PySide2/CMakeLists.txt | 5 | ||||
-rw-r--r-- | sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml | 3 | ||||
-rw-r--r-- | sources/pyside2/PySide2/__init__.py.in | 7 | ||||
-rw-r--r-- | sources/pyside2/PySide2/_config.py.in | 11 | ||||
-rw-r--r-- | sources/pyside2/PySide2/typesystem_templates.xml | 6 | ||||
-rw-r--r-- | sources/pyside2/pyside_version.py | 49 | ||||
-rw-r--r-- | sources/pyside2/tests/QtWidgets/qvalidator_test.py | 27 | ||||
-rw-r--r-- | sources/pyside2/tests/pysidetest/version_test.py | 6 | ||||
-rw-r--r-- | sources/shiboken2/CMakeLists.txt | 60 | ||||
-rw-r--r-- | sources/shiboken2/shiboken_version.py | 49 | ||||
-rw-r--r-- | utils.py | 35 |
14 files changed, 715 insertions, 332 deletions
@@ -37,25 +37,29 @@ ## ############################################################################# -import os, sys -import subprocess +import os, sys, re, subprocess from distutils.spawn import find_executable class QtInfo(object): def __init__(self, qmake_command=None): + self.initialized = False + if qmake_command: self._qmake_command = qmake_command else: self._qmake_command = [find_executable("qmake"),] - self._dict = {} - # bind all variables early at __init__ time. - for thing in self.__class__.__dict__: - getattr(self, thing) + + # Dict to cache qmake values. + self._query_dict = {} + # Dict to cache mkspecs variables. + self._mkspecs_dict = {} + # Initialize the properties. + self._initProperties() def getQMakeCommand(self): qmake_command_string = self._qmake_command[0] for entry in self._qmake_command[1:]: - qmake_command_string += " %s" %(entry) + qmake_command_string += " {}".format(entry) return qmake_command_string def getVersion(self): @@ -91,21 +95,127 @@ class QtInfo(object): def getQmlPath(self): return self.getProperty("QT_INSTALL_QML") - def _getProperty(self, prop_name): - cmd = self._qmake_command + ["-query", prop_name] + def getMacOSMinDeploymentTarget(self): + """ Return value is a macOS version or None. """ + return self.getProperty("QMAKE_MACOSX_DEPLOYMENT_TARGET") + + def getBuildType(self): + """ Return value is either debug, release, debug_release, or None. """ + return self.getProperty("BUILD_TYPE") + + def getSrcDir(self): + """ Return path to Qt src dir or None.. """ + return self.getProperty("QT_INSTALL_PREFIX/src") + + def getProperty(self, prop_name): + if prop_name not in self._query_dict: + return None + return self._query_dict[prop_name] + + def getProperties(self): + return self._query_dict + + def getMkspecsVariables(self): + return self._mkspecs_dict + + def _getQMakeOutput(self, args_list = []): + cmd = self._qmake_command + args_list proc = subprocess.Popen(cmd, stdout = subprocess.PIPE, shell=False) - prop = proc.communicate()[0] + output = proc.communicate()[0] proc.wait() if proc.returncode != 0: return None if sys.version_info >= (3,): - return str(prop, 'ascii').strip() - return prop.strip() + output = str(output, 'ascii').strip() + else: + output = output.strip() + return output + + def _parseQueryProperties(self, process_output): + props = {} + if not process_output: + return props + lines = [s.strip() for s in process_output.splitlines()] + for line in lines: + if line and ':' in line: + key, value = line.split(':', 1) + props[key] = value + return props + + def _getQueryProperties(self): + output = self._getQMakeOutput(['-query']) + self._query_dict = self._parseQueryProperties(output) + + def _parseQtBuildType(self): + key = 'QT_CONFIG' + if key not in self._mkspecs_dict: + return None - def getProperty(self, prop_name): - if prop_name not in self._dict: - self._dict[prop_name] = self._getProperty(prop_name) - return self._dict[prop_name] + qt_config = self._mkspecs_dict[key] + if 'debug_and_release' in qt_config: + return 'debug_and_release' + + split = qt_config.split(' ') + if 'release' in split and 'debug' in split: + return 'debug_and_release' + + if 'release' in split: + return 'release' + + if 'debug' in split: + return 'debug' + + return None + + def _getOtherProperties(self): + # Get the src property separately, because it is not returned by qmake unless explicitly + # specified. + key = 'QT_INSTALL_PREFIX/src' + result = self._getQMakeOutput(['-query', key]) + self._query_dict[key] = result + + # Get mkspecs variables and cache them. + self._getQMakeMkspecsVariables() + + # Get macOS minimum deployment target. + key = 'QMAKE_MACOSX_DEPLOYMENT_TARGET' + if key in self._mkspecs_dict: + self._query_dict[key] = self._mkspecs_dict[key] + + # Figure out how Qt was built: debug mode, release mode, or both. + build_type = self._parseQtBuildType() + if build_type: + self._query_dict['BUILD_TYPE'] = build_type + + def _initProperties(self): + self._getQueryProperties() + self._getOtherProperties() + + def _getQMakeMkspecsVariables(self): + # Create empty temporary qmake project file. + temp_file_name = 'qmake_fake_empty_project.txt' + open(temp_file_name, 'a').close() + + # Query qmake for all of its mkspecs variables. + qmakeOutput = self._getQMakeOutput(['-E', temp_file_name]) + lines = [s.strip() for s in qmakeOutput.splitlines()] + pattern = re.compile(r"^(.+?)=(.+?)$") + for line in lines: + found = pattern.search(line) + if found: + key = found.group(1).strip() + value = found.group(2).strip() + self._mkspecs_dict[key] = value + + # We need to clean up after qmake, which always creates a .qmake.stash file after a -E + # invocation. + qmake_stash_file = os.path.join(os.getcwd(), ".qmake.stash") + if os.path.exists(qmake_stash_file): + os.remove(qmake_stash_file) + + # Also clean up the temporary empty project file. + if os.path.exists(temp_file_name): + os.remove(temp_file_name) version = property(getVersion) bins_dir = property(getBinsPath) @@ -119,3 +229,6 @@ class QtInfo(object): headers_dir = property(getHeadersPath) docs_dir = property(getDocsPath) qml_dir = property(getQmlPath) + macos_min_deployment_target = property(getMacOSMinDeploymentTarget) + build_type = property(getBuildType) + src_dir = property(getSrcDir) @@ -82,6 +82,7 @@ For development purposes the following options might be of use, when using "setu --skip-packaging will skip creation of the python package, --ignore-git will skip the fetching and checkout steps for supermodule and all submodules. --verbose-build will output the compiler invocation with command line arguments, etc. + --sanitize-address will build all targets with address sanitizer enabled. REQUIREMENTS: - Python: 2.6, 2.7, 3.3, 3.4, 3.5 and 3.6 are supported @@ -109,75 +110,60 @@ OS X SDK: You can specify which OS X SDK should be used for compilation with the For e.g. "--osx-sysroot=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/". OS X Minimum deployment target: - You can specify the OS X minimum deployment target with the --osx-deployment-target=<x> option. - For example "--osx-deployment-target=10.10". + You can specify a custom OS X minimum deployment target with the --osx-deployment-target=<value> + option. + For example: "--osx-deployment-target=10.10". + + If the option is not set, the minimum deployment target of the used Qt library will be used + instead. Thus it is not necessary to use the option without a good reason. + If a new value is specified, it has to be higher or equal to both Python's and Qt's minimum + deployment targets. + + Description: + OS X allows specifying a minimum OS version on which a binary will be able to run. This implies + that an application can be built on a machine with the latest OS X version installed, with + latest Xcode version and SDK version and the built application can still run on an older OS + version. +""" - OS X provides the ability to set what is the minimum OS version on which a binary will run. This - means that a build can be done on the latest OS X version with latest XCode and SDK versions, - but the built application / library can run on older OS versions. +import os +import time +from utils import memoize, has_option, get_python_dict +OPTION_SNAPSHOT_BUILD = has_option("snapshot-build") +script_dir = os.getcwd() - Note: if the option is not set, CMake will try to query the MACOSX_DEPLOYMENT_TARGET environment - variable, and if that is empty, it will try to deduce a value internally (afaik based on - current OS X version and on the chosen SDK version). -""" +@memoize +def get_package_timestamp(): + return int(time.time()) -__version__ = "5.9" +@memoize +def get_package_version(): + """ Returns the version string for the PySide2 package. """ + pyside_version_py = os.path.join(script_dir, "sources", "pyside2", "pyside_version.py") + d = get_python_dict(pyside_version_py) -containedModules = ['shiboken2', 'pyside2'] + final_version = "{}.{}.{}".format(d['major_version'], d['minor_version'], d['patch_version']) + pre_release_version_type = d['pre_release_version_type'] + pre_release_version = d['pre_release_version'] + if pre_release_version and pre_release_version: + final_version += pre_release_version_type + pre_release_version -submodules = { - '2.0.0.dev0': [ - ["pyside2-tools", "dev"] - ], - '5.9': [ - ["pyside2-tools", "5.9"] - ], - '5.6': [ - ["pyside2-tools", "5.6"], - ["pyside2-examples", "5.6"], - ["wiki", "master", ".."] - ], -} -old_submodules = { - # these are just kept a while for reference but not maintained. - # if you need an old version, please use the pyside/pyside-setup version. - '1.3.0dev': [ - ["shiboken", "master"], - ["pyside", "master"], - ["pyside-tools", "master"], - ["pyside-examples", "master"], - ], - '1.2.2': [ - ["shiboken", "1.2.2"], - ["pyside", "1.2.2"], - ["pyside-tools", "0.2.15"], - ["pyside-examples", "master"], - ], - '1.2.1': [ - ["shiboken", "1.2.1"], - ["pyside", "1.2.1"], - ["pyside-tools", "0.2.15"], - ["pyside-examples", "master"], - ], - '1.2.0': [ - ["shiboken", "1.2.0"], - ["pyside", "1.2.0"], - ["pyside-tools", "0.2.14"], - ["pyside-examples", "master"], - ], - '1.1.2': [ - ["shiboken", "1.1.2"], - ["pyside", "1.1.2"], - ["pyside-tools", "0.2.14"], - ["pyside-examples", "master"], - ], - '1.1.1': [ - ["shiboken", "1.1.1"], - ["pyside", "1.1.1"], - ["pyside-tools", "0.2.14"], - ["pyside-examples", "master"], - ], -} + # Add the current timestamp to the version number, to suggest it is a development snapshot + # build. + if OPTION_SNAPSHOT_BUILD: + final_version += ".dev{}".format(get_package_timestamp()) + return final_version + +# The __version__ variable is just for PEP compliancy, and shoudn't be used as a value source. +__version__ = get_package_version() + +# Buildable extensions. +containedModules = ['shiboken2', 'pyside2', 'pyside2-tools'] + +# Git submodules: ["submodule_name", "location_relative_to_sources_folder"] +submodules = [["pyside2-tools"], + ["pyside2-examples"], + ["wiki", ".."]] pyside_package_dir_name = "pyside_package" @@ -187,14 +173,13 @@ except ImportError: from ez_setup import use_setuptools use_setuptools() -import os import sys import platform -import time import re import fnmatch import difflib # for a close match of dirname and module +import functools from distutils import log from distutils.errors import DistutilsOptionError @@ -227,7 +212,6 @@ from utils import makefile from utils import copyfile from utils import copydir from utils import run_process_output, run_process -from utils import has_option from utils import option_value from utils import update_env_path from utils import init_msvc_env @@ -236,12 +220,13 @@ from utils import filter_match from utils import osx_fix_rpaths_for_library from utils import copy_icu_libs from utils import find_files_using_glob + from textwrap import dedent # guess a close folder name for extensions def get_extension_folder(ext): - candidates = containedModules - for gitModules in submodules[__version__]: + candidates = list(containedModules) + for gitModules in submodules: candidates.append(gitModules[0]) folder = difflib.get_close_matches(ext, candidates)[0] return folder @@ -276,8 +261,8 @@ OPTION_CMAKE = option_value("cmake") OPTION_OPENSSL = option_value("openssl") OPTION_ONLYPACKAGE = has_option("only-package") OPTION_STANDALONE = has_option("standalone") -OPTION_VERSION = option_value("version") -OPTION_LISTVERSIONS = has_option("list-versions") +OPTION_VERSION = option_value("version") # Deprecated +OPTION_LISTVERSIONS = has_option("list-versions") # Deprecated OPTION_MAKESPEC = option_value("make-spec") OPTION_IGNOREGIT = has_option("ignore-git") OPTION_NOEXAMPLES = has_option("no-examples") # don't include pyside2-examples @@ -300,6 +285,7 @@ OPTION_QT_CONF_PREFIX = option_value("qt-conf-prefix") OPTION_QT_SRC = option_value("qt-src-dir") OPTION_ICULIB = option_value("iculib-url") # Deprecated OPTION_VERBOSE_BUILD = has_option("verbose-build") +OPTION_SANITIZE_ADDRESS = has_option("sanitize-address") # This is used automatically by distutils.command.install object, to specify final installation # location. @@ -370,14 +356,6 @@ if OPTION_ICULIB: if not OPTION_STANDALONE: print("--iculib-url option is a no-op option and will be removed soon.") -# Show available versions -if OPTION_LISTVERSIONS: - for v in submodules: - print("%s" % (v)) - for m in submodules[v]: - print(" %s %s" % (m[0], m[1])) - sys.exit(1) - # Change the cwd to our source dir try: this_file = __file__ @@ -386,25 +364,16 @@ except NameError: this_file = os.path.abspath(this_file) if os.path.dirname(this_file): os.chdir(os.path.dirname(this_file)) -script_dir = os.getcwd() - -# Change package version -if OPTION_VERSION: - if OPTION_IGNOREGIT: - print("Option --version can not be used together with option --ignore-git") - sys.exit(1) - if not os.path.isdir(".git"): - print("Option --version is available only when pyside2-setup was cloned from git repository") - sys.exit(1) - if not OPTION_VERSION in submodules: - print("""Invalid version specified %s -Use --list-versions option to get list of available versions""" % OPTION_VERSION) - sys.exit(1) - __version__ = OPTION_VERSION def is_debug_python(): return getattr(sys, "gettotalrefcount", None) is not None +if OPTION_NOEXAMPLES: + # Remove pyside2-examples from submodules so they will not be included. + for idx, item in enumerate(submodules): + if item[0].startswith('pyside2-examples'): + del submodules[idx] + # Return a prefix suitable for the _install/_build directory def prefix(): virtualEnvName = os.environ.get('VIRTUAL_ENV', None) @@ -418,65 +387,56 @@ def prefix(): # Initialize, pull and checkout submodules def prepareSubModules(): - print("Initializing submodules for PySide2 version %s" % __version__) + print("Initializing submodules for PySide2 version: {}".format(get_package_version())) submodules_dir = os.path.join(script_dir, "sources") + # Create list of [name, desired branch, absolute path, desired branch] # and determine whether all submodules are present needInitSubModules = False - modulesList = [] - for m in submodules[__version__]: + + for m in submodules: module_name = m[0] - module_version = m[1] - module_dir = m[2] if len(m) > 2 else '' + module_dir = m[1] if len(m) > 1 else '' module_dir = os.path.join(submodules_dir, module_dir, module_name) # Check for non-empty directory (repository checked out) if not os.listdir(module_dir): needInitSubModules = True - modulesList.append([module_name, module_version, module_dir]) + break + if needInitSubModules: git_update_cmd = ["git", "submodule", "update", "--init"] if run_process(git_update_cmd) != 0: - raise DistutilsSetupError("Failed to initialize the git submodules") + m = "Failed to initialize the git submodules: update --init failed" + raise DistutilsSetupError(m) git_pull_cmd = ["git", "submodule", "foreach", "git", "fetch", "--all"] if run_process(git_pull_cmd) != 0: - raise DistutilsSetupError("Failed to initialize the git submodules") + m = "Failed to initialize the git submodules: git fetch --all failed" + raise DistutilsSetupError(m) else: - print("All submodules present...") - # Ensure all submodules have the correct branch checked out - for m in modulesList: - module_name = m[0] - module_version = m[1] - module_dir = m[2] - os.chdir(module_dir) - currentBranch = '' - branches = set() - for line in run_process_output(['git', 'branch']): - if line.startswith('* '): - currentBranch = line[2:len(line)] - else: - branches.add(line.strip()) - if currentBranch != module_version: - if not module_version in branches: - print("Creating tracking branch %s for submodule %s" % \ - (module_version, module_name)) - git_create_branch_cmd = ["git", "branch", "--track", module_version, - "origin/" + module_version] - if run_process(git_create_branch_cmd) != 0: - raise DistutilsSetupError("Failed to create a tracking branch %s for %s" % \ - (module_version, module_name)) - print("Checking out submodule %s to branch %s (from %s)" % (module_name, module_version, currentBranch)) - git_checkout_cmd = ["git", "checkout", module_version] - if run_process(git_checkout_cmd) != 0: - raise DistutilsSetupError("Failed to initialize the git submodule %s" % module_name) - else: - print("Submodule %s has branch %s checked out" % (module_name, module_version)) - os.chdir(script_dir) + print("All submodules present.") + + git_update_cmd = ["git", "submodule", "update"] + if run_process(git_update_cmd) != 0: + m = "Failed to checkout the correct git submodules SHA1s." + raise DistutilsSetupError(m) + +# Single global instance of QtInfo to be used later in multiple code paths. +qtinfo = QtInfo(QMAKE_COMMAND) + +def get_qt_version(): + qt_version = qtinfo.version + + if not qt_version: + log.error("Failed to query the Qt version with qmake %s" % self.qtinfo.qmake_command) + sys.exit(1) + + return qt_version def prepareBuild(): if os.path.isdir(".git") and not OPTION_IGNOREGIT and not OPTION_ONLYPACKAGE and not OPTION_REUSE_BUILD: prepareSubModules() # Clean up temp and package folders - for n in [pyside_package_dir_name, "build", "PySide2-%s" % __version__]: + for n in [pyside_package_dir_name, "build"]: d = os.path.join(script_dir, n) if os.path.isdir(d): print("Removing %s" % d) @@ -492,29 +452,14 @@ def prepareBuild(): os.makedirs(pkg_dir) # locate Qt sources for the documentation if OPTION_QT_SRC is None: - qmakeOutput = run_process_output([OPTION_QMAKE, '-query', 'QT_INSTALL_PREFIX']) - if qmakeOutput: + installPrefix = qtinfo.prefix_dir + if installPrefix: global qtSrcDir - installPrefix = qmakeOutput[0].rstrip() if installPrefix.endswith("qtbase"): # In-source, developer build qtSrcDir = installPrefix else: # SDK: Use 'Src' directory qtSrcDir = os.path.join(os.path.dirname(installPrefix), 'Src', 'qtbase') -def get_qt_version(computed_qtinfo = None): - if not computed_qtinfo: - qtinfo = QtInfo(QMAKE_COMMAND) - else: - qtinfo = computed_qtinfo - - qt_version = qtinfo.version - - if not qt_version: - log.error("Failed to query the Qt version with qmake %s" % self.qtinfo.qmake_command) - sys.exit(1) - - return qt_version - class pyside_install(_install): def __init__(self, *args, **kwargs): _install.__init__(self, *args, **kwargs) @@ -577,7 +522,8 @@ if wheel_module_exists: # Example: PySide2-5.6-5.6.4-cp27-cp27m-macosx_10_10_intel.whl # The PySide2 version is "5.6. The built against Qt version is "5.6.4. qt_version = get_qt_version() - wheel_version = "{}-{}".format(__version__, qt_version) + package_version = get_package_version() + wheel_version = "{}-{}".format(package_version, qt_version) components = (_safer_name(self.distribution.get_name()), wheel_version) if self.build_number: @@ -668,7 +614,7 @@ class pyside_build(_build): platform_arch = platform.architecture()[0] log.info("Python architecture is %s" % platform_arch) - build_type = OPTION_DEBUG and "Debug" or "Release" + build_type = "Debug" if OPTION_DEBUG else "Release" if OPTION_RELWITHDEBINFO: build_type = 'RelWithDebInfo' @@ -823,9 +769,9 @@ class pyside_build(_build): log.error("Failed to locate a dynamic Python library, using %s" % py_library) - self.qtinfo = QtInfo(QMAKE_COMMAND) + self.qtinfo = qtinfo qt_dir = os.path.dirname(OPTION_QMAKE) - qt_version = get_qt_version(self.qtinfo) + qt_version = get_qt_version() # Update the PATH environment variable additionalPaths = [py_scripts_dir, qt_dir] @@ -882,7 +828,7 @@ class pyside_build(_build): self.shiboken_build_dir = os.path.join(self.build_dir, "shiboken2") log.info("=" * 30) - log.info("Package version: %s" % __version__) + log.info("Package version: %s" % get_package_version()) log.info("Build type: %s" % self.build_type) log.info("Build tests: %s" % self.build_tests) log.info("-" * 3) @@ -926,6 +872,9 @@ class pyside_build(_build): log.info("-" * 3) if sys.platform == 'win32': log.info("OpenSSL dll directory: %s" % OPTION_OPENSSL) + if sys.platform == 'darwin': + pyside_macos_deployment_target = pyside_build.macos_pyside_min_deployment_target() + log.info("MACOSX_DEPLOYMENT_TARGET set to: {}".format(pyside_macos_deployment_target)) log.info("=" * 30) # Prepare folders @@ -941,7 +890,7 @@ class pyside_build(_build): if not OPTION_ONLYPACKAGE: # Build extensions - for ext in containedModules + ['pyside2-tools']: + for ext in containedModules: self.build_extension(ext) if OPTION_BUILDTESTS: @@ -970,20 +919,57 @@ class pyside_build(_build): log.info('*** Build completed') @staticmethod - def macos_min_deployment_target(): - # If no explicit minimum deployment target is provided to setup.py, then use the current - # build OS version. Otherwise use the provided version. - current_os_version, _, _ = platform.mac_ver() - current_os_version = '.'.join(current_os_version.split('.')[:2]) - deployment_target = current_os_version - if OPTION_OSX_DEPLOYMENT_TARGET: - deployment_target = OPTION_OSX_DEPLOYMENT_TARGET + def macos_qt_min_deployment_target(): + target = qtinfo.macos_min_deployment_target + + if not target: + raise DistutilsSetupError("Failed to query for Qt's QMAKE_MACOSX_DEPLOYMENT_TARGET.") + return target - return deployment_target + @staticmethod + @memoize + def macos_pyside_min_deployment_target(): + """ + Compute and validate PySide2 MACOSX_DEPLOYMENT_TARGET value. Candidate sources that are + considered: + - setup.py provided value + - maximum value between minimum deployment target of the Python interpreter and the + minimum deployment target of the Qt libraries. + If setup.py value is provided, that takes precedence. + Otherwise use the maximum of the above mentioned two values. + """ + python_target = get_config_var('MACOSX_DEPLOYMENT_TARGET') or None + qt_target = pyside_build.macos_qt_min_deployment_target() + setup_target = OPTION_OSX_DEPLOYMENT_TARGET + + qt_target_split = [int(x) for x in qt_target.split('.')] + if python_target: + python_target_split = [int(x) for x in python_target.split('.')] + if setup_target: + setup_target_split = [int(x) for x in setup_target.split('.')] + + message = "Can't set MACOSX_DEPLOYMENT_TARGET value to {} because " \ + "{} was built with minimum deployment target set to {}." + # setup.py provided OPTION_OSX_DEPLOYMENT_TARGET value takes precedence. + if setup_target: + if python_target and setup_target_split < python_target_split: + raise DistutilsSetupError(message.format(setup_target, "Python", python_target)) + if setup_target_split < qt_target_split: + raise DistutilsSetupError(message.format(setup_target, "Qt", qt_target)) + # All checks clear, use setup.py provided value. + return setup_target + + # Setup.py value not provided, use same value as provided by Qt. + if python_target: + maximum_target = '.'.join([str(e) for e in max(python_target_split, qt_target_split)]) + else: + maximum_target = qt_target + return maximum_target @staticmethod + @memoize def macos_plat_name(): - deployment_target = pyside_build.macos_min_deployment_target() + deployment_target = pyside_build.macos_pyside_min_deployment_target() # Example triple "macosx-10.12-x86_64". plat = get_platform().split("-") plat_name = "{}-{}-{}".format(plat[0], deployment_target, plat[2]) @@ -1068,6 +1054,13 @@ class pyside_build(_build): if OPTION_VERBOSE_BUILD: cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON") + if OPTION_SANITIZE_ADDRESS: + # Some simple sanity checking. Only use at your own risk. + if sys.platform.startswith('linux') or sys.platform.startswith('darwin'): + cmake_cmd.append("-DSANITIZE_ADDRESS=ON") + else: + raise DistutilsSetupError("Address sanitizer can only be used on Linux and macOS.") + if extension.lower() == "pyside2": pyside_qt_conf_prefix = '' if OPTION_QT_CONF_PREFIX: @@ -1079,6 +1072,17 @@ class pyside_build(_build): pyside_qt_conf_prefix = '"."' cmake_cmd.append("-DPYSIDE_QT_CONF_PREFIX=%s" % pyside_qt_conf_prefix) + # Pass package version to CMake, so this string can be embedded into _config.py file. + package_version = get_package_version() + cmake_cmd.append("-DPYSIDE_SETUP_PY_PACKAGE_VERSION={0}".format(package_version)) + + # In case if this is a snapshot build, also pass the timestamp as a separate value, + # because it the only version component that is actually generated by setup.py. + timestamp = '' + if OPTION_SNAPSHOT_BUILD: + timestamp = get_package_timestamp() + cmake_cmd.append("-DPYSIDE_SETUP_PY_PACKAGE_TIMESTAMP={0}".format(timestamp)) + if extension.lower() == "shiboken2": cmake_cmd.append("-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=yes") if sys.version_info[0] > 2: @@ -1111,7 +1115,7 @@ class pyside_build(_build): # set its own minimum deployment target environment variable which is # based on the python interpreter sysconfig value. Doing so could break the # detected clang include paths for example. - deployment_target = pyside_build.macos_min_deployment_target() + deployment_target = pyside_build.macos_pyside_min_deployment_target() cmake_cmd.append("-DCMAKE_OSX_DEPLOYMENT_TARGET={0}".format(deployment_target)) os.environ['MACOSX_DEPLOYMENT_TARGET'] = deployment_target @@ -1153,8 +1157,6 @@ class pyside_build(_build): def prepare_packages(self): try: log.info("Preparing packages...") - version_str = "%sqt%s%s" % (__version__, self.qtinfo.version.replace(".", "")[0:3], - self.debug and "dbg" or "") vars = { "site_packages_dir": self.site_packages_dir, "sources_dir": self.sources_dir, @@ -1173,7 +1175,6 @@ class pyside_build(_build): "qt_prefix_dir": self.qtinfo.prefix_dir, "qt_translations_dir": self.qtinfo.translations_dir, "qt_qml_dir": self.qtinfo.qml_dir, - "version": version_str, } os.chdir(self.script_dir) @@ -1190,20 +1191,8 @@ class pyside_build(_build): # Get config that contains list of built modules, and SOVERSIONs of the built libraries. pyside_package_dir = vars['pyside_package_dir'] config_path = os.path.join(pyside_package_dir, "PySide2", "_config.py") - - try: - with open(config_path) as f: - scoped_locals = {} - code = compile(f.read(), config_path, 'exec') - exec(code, scoped_locals, scoped_locals) - config = {} - config['built_modules'] = scoped_locals['built_modules'] - config['shiboken_library_soversion'] = scoped_locals['shiboken_library_soversion'] - config['pyside_library_soversion'] = scoped_locals['pyside_library_soversion'] - return config - except IOError as e: - print("get_built_pyside_config: Couldn't find file: {}.".format(config_path)) - raise + config = get_python_dict(config_path) + return config def prepare_packages_posix(self, vars): executables = [] @@ -1371,7 +1360,7 @@ class pyside_build(_build): # <qt>/translations/* -> <setup>/PySide2/Qt/translations copydir("{qt_translations_dir}", "{pyside_package_dir}/PySide2/Qt/translations", - filter=["*.qm"], + filter=["*.qm", "*.pak"], force=False, vars=vars) @@ -1460,12 +1449,16 @@ class pyside_build(_build): # <qt>/translations/* -> <setup>/PySide2/Qt/translations copydir("{qt_translations_dir}", "{pyside_package_dir}/PySide2/Qt/translations", - filter=["*.qm"], + filter=["*.qm", "*.pak"], force=False, vars=vars) def prepare_packages_win32(self, vars): - pdbs = ['*.pdb'] if self.debug or self.build_type == 'RelWithDebInfo' else [] + # For now, debug symbols will not be shipped into the package. + copy_pdbs = False + pdbs = [] + if (self.debug or self.build_type == 'RelWithDebInfo') and copy_pdbs: + pdbs = ['*.pdb'] # <install>/lib/site-packages/PySide2/* -> <setup>/PySide2 copydir( "{site_packages_dir}/PySide2", @@ -1473,18 +1466,19 @@ class pyside_build(_build): vars=vars) built_modules = self.get_built_pyside_config(vars)['built_modules'] - if self.debug or self.build_type == 'RelWithDebInfo': - # <build>/pyside2/PySide2/*.pdb -> <setup>/PySide2 - copydir( - "{build_dir}/pyside2/PySide2", - "{pyside_package_dir}/PySide2", - filter=pdbs, - recursive=False, vars=vars) + # <build>/pyside2/PySide2/*.pdb -> <setup>/PySide2 + copydir( + "{build_dir}/pyside2/PySide2", + "{pyside_package_dir}/PySide2", + filter=pdbs, + recursive=False, vars=vars) + # <build>/shiboken2/doc/html/* -> <setup>/PySide2/docs/shiboken2 copydir( "{build_dir}/shiboken2/doc/html", "{pyside_package_dir}/PySide2/docs/shiboken2", force=False, vars=vars) + # <install>/lib/site-packages/shiboken2.pyd -> <setup>/PySide2/shiboken2.pyd shiboken_module_name = 'shiboken2.pyd' shiboken_src_path = "{site_packages_dir}".format(**vars) @@ -1497,12 +1491,14 @@ class pyside_build(_build): "{site_packages_dir}/{shiboken_module_name}", "{pyside_package_dir}/PySide2/{shiboken_module_name}", vars=vars) - if self.debug or self.build_type == 'RelWithDebInfo': - copydir( - "{build_dir}/shiboken2/shibokenmodule", - "{pyside_package_dir}/PySide2", - filter=pdbs, - recursive=False, vars=vars) + # @TODO: Fix this .pdb file not to overwrite release {shibokengenerator}.pdb file. + # Task-number: PYSIDE-615 + copydir( + "{build_dir}/shiboken2/shibokenmodule", + "{pyside_package_dir}/PySide2", + filter=pdbs, + recursive=False, vars=vars) + # <install>/lib/site-packages/pyside2uic/* -> <setup>/pyside2uic copydir( "{site_packages_dir}/pyside2uic", @@ -1512,6 +1508,7 @@ class pyside_build(_build): rmtree("{pyside_package_dir}/pyside2uic/port_v2".format(**vars)) else: rmtree("{pyside_package_dir}/pyside2uic/port_v3".format(**vars)) + # <install>/bin/pyside2-uic -> PySide2/scripts/uic.py makefile( "{pyside_package_dir}/PySide2/scripts/__init__.py", @@ -1520,33 +1517,46 @@ class pyside_build(_build): "{install_dir}/bin/pyside2-uic", "{pyside_package_dir}/PySide2/scripts/uic.py", force=False, vars=vars) + # <install>/bin/*.exe,*.dll,*.pdb -> PySide2/ copydir( "{install_dir}/bin/", "{pyside_package_dir}/PySide2", - filter=["*.exe", "*.dll"] + pdbs, + filter=["*.exe", "*.dll"], + recursive=False, vars=vars) + # @TODO: Fix this .pdb file not to overwrite release {shibokenmodule}.pdb file. + # Task-number: PYSIDE-615 + copydir( + "{build_dir}/shiboken2/generator", + "{pyside_package_dir}/PySide2", + filter=pdbs, recursive=False, vars=vars) + # <install>/lib/*.lib -> PySide2/ copydir( "{install_dir}/lib/", "{pyside_package_dir}/PySide2", filter=["*.lib"], recursive=False, vars=vars) + # <install>/share/PySide2/typesystems/* -> <setup>/PySide2/typesystems copydir( "{install_dir}/share/PySide2/typesystems", "{pyside_package_dir}/PySide2/typesystems", vars=vars) + # <install>/include/* -> <setup>/PySide2/include copydir( "{install_dir}/include", "{pyside_package_dir}/PySide2/include", vars=vars) + # <source>/pyside2/PySide2/support/* -> <setup>/PySide2/support/* copydir( "{build_dir}/pyside2/PySide2/support", "{pyside_package_dir}/PySide2/support", vars=vars) + if not OPTION_NOEXAMPLES: # examples/* -> <setup>/PySide2/examples copydir(os.path.join(self.script_dir, "examples"), @@ -1559,6 +1569,7 @@ class pyside_build(_build): pyside_rcc_options = '-py3' regenerate_qt_resources(examples_path, pyside_rcc_path, pyside_rcc_options) + # <ssl_libs>/* -> <setup>/PySide2/openssl copydir("{ssl_libs_dir}", "{pyside_package_dir}/PySide2/openssl", filter=[ @@ -1566,60 +1577,100 @@ class pyside_build(_build): "ssleay32.dll"], force=False, vars=vars) - # <qt>/bin/*.dll -> <setup>/PySide2 + # <qt>/bin/*.dll and Qt *.exe -> <setup>/PySide2 + qt_artifacts_permanent = [ + "opengl*.dll", + "d3d*.dll", + "designer.exe", + "linguist.exe", + "lrelease.exe", + "lupdate.exe", + "lconvert.exe", + "qtdiag.exe" + ] copydir("{qt_bin_dir}", "{pyside_package_dir}/PySide2", - filter=[ - "*.dll", - "designer.exe", - "linguist.exe", - "lrelease.exe", - "lupdate.exe", - "lconvert.exe"], - ignore=["*d4.dll"], + filter=qt_artifacts_permanent, recursive=False, vars=vars) - if self.debug: - # <qt>/bin/*d4.dll -> <setup>/PySide2 - copydir("{qt_bin_dir}", "{pyside_package_dir}/PySide2", - filter=["*d4.dll"] + pdbs, - recursive=False, vars=vars) - if self.debug or self.build_type == 'RelWithDebInfo': - # <qt>/lib/*.pdb -> <setup>/PySide2 - copydir("{qt_lib_dir}", "{pyside_package_dir}/PySide2", - filter=["*.pdb"], - recursive=False, vars=vars) + # <qt>/bin/*.dll and Qt *.pdbs -> <setup>/PySide2 part two + # File filter to copy only debug or only release files. + qt_dll_patterns = ["Qt5*{}.dll", "lib*{}.dll"] + if copy_pdbs: + qt_dll_patterns += ["Qt5*{}.pdb", "lib*{}.pdb"] + def qt_build_config_filter(patterns, file_name, file_full_path): + release = [a.format('') for a in patterns] + debug = [a.format('d') for a in patterns] + + # If qt is not a debug_and_release build, that means there is only one set of shared + # libraries, so we can just copy them. + if qtinfo.build_type != 'debug_and_release': + if filter_match(file_name, release): + return True + return False + + # In debug_and_release case, choosing which files to copy is more difficult. We want + # to copy only the files that match the PySide2 build type. So if PySide2 is built in + # debug mode, we want to copy only Qt debug libraries (ending with "d.dll"). Or vice + # versa. The problem is that some libraries have "d" as the last character of the actual + # library name (for example Qt5Gamepad.dll and Qt5Gamepadd.dll). So we can't just + # match a pattern ending in "d". Instead we check if there exists a file with the same + # name plus an additional "d" at the end, and using that information we can judge if + # the currently processed file is a debug or release file. + + # e.g. ["Qt5Cored", ".dll"] + file_split = os.path.splitext(file_name) + file_base_name = file_split[0] + file_ext = file_split[1] + # e.g. "/home/work/qt/qtbase/bin" + file_path_dir_name = os.path.dirname(file_full_path) + # e.g. "Qt5Coredd" + maybe_debug_name = file_base_name + 'd' + if self.debug: + filter = debug + def predicate(path): return not os.path.exists(path) + else: + filter = release + def predicate(path): return os.path.exists(path) + # e.g. "/home/work/qt/qtbase/bin/Qt5Coredd.dll" + other_config_path = os.path.join(file_path_dir_name, maybe_debug_name + file_ext) + + if filter_match(file_name, filter) and predicate(other_config_path): + return True + return False - # I think these are the qt-mobility DLLs, at least some are, - # so let's copy them too - # <qt>/lib/*.dll -> <setup>/PySide2 - copydir("{qt_lib_dir}", "{pyside_package_dir}/PySide2", - filter=["*.dll"], - ignore=["*d?.dll"], + qt_dll_filter = functools.partial(qt_build_config_filter, qt_dll_patterns) + copydir("{qt_bin_dir}", "{pyside_package_dir}/PySide2", + file_filter_function=qt_dll_filter, recursive=False, vars=vars) - if self.debug: - # <qt>/lib/*d4.dll -> <setup>/PySide2 - copydir("{qt_lib_dir}", "{pyside_package_dir}/PySide2", - filter=["*d?.dll"], - recursive=False, vars=vars) - if self.debug or self.build_type == 'RelWithDebInfo': - # <qt>/lib/*pdb -> <setup>/PySide2 - copydir("{qt_lib_dir}", "{pyside_package_dir}/PySide2", - filter=pdbs, - recursive=False, vars=vars) # <qt>/plugins/* -> <setup>/PySide2/plugins + plugin_dll_patterns = ["*{}.dll"] + if copy_pdbs: + plugin_dll_patterns += ["*{}.pdb"] + plugin_dll_filter = functools.partial(qt_build_config_filter, plugin_dll_patterns) copydir("{qt_plugins_dir}", "{pyside_package_dir}/PySide2/plugins", - filter=["*.dll"] + pdbs, + file_filter_function=plugin_dll_filter, vars=vars) + # <qt>/translations/* -> <setup>/PySide2/translations copydir("{qt_translations_dir}", "{pyside_package_dir}/PySide2/translations", - filter=["*.qm"], + filter=["*.qm", "*.pak"], force=False, vars=vars) # <qt>/qml/* -> <setup>/PySide2/qml + qml_dll_patterns = ["*{}.dll"] + if copy_pdbs: + qml_dll_patterns += ["*{}.pdb"] + qml_ignore = [a.format('') for a in qml_dll_patterns] + qml_dll_filter = functools.partial(qt_build_config_filter, qml_dll_patterns) copydir("{qt_qml_dir}", "{pyside_package_dir}/PySide2/qml", - filter=None, + ignore=qml_ignore, + force=False, + recursive=True, + vars=vars) + copydir("{qt_qml_dir}", "{pyside_package_dir}/PySide2/qml", + file_filter_function=qml_dll_filter, force=False, recursive=True, vars=vars) @@ -1630,30 +1681,24 @@ class pyside_build(_build): recursive=False, vars=vars) + filter = 'QtWebEngineProcess{}.exe'.format('d' if self.debug else '') copydir("{qt_bin_dir}", "{pyside_package_dir}/PySide2", - filter=["QtWebEngineProcess*.exe"], + filter=[filter], recursive=False, vars=vars) self.prepare_standalone_clang(is_win=True) # pdb files for libshiboken and libpyside - if self.debug or self.build_type == 'RelWithDebInfo': - # XXX dbgPostfix gives problems - the structure in shiboken2/data should be re-written! - # Not sure what the above refers to, but because both the extension module - # (shiboken2.pyd) and the shared library (shiboken2.dll) have the same basename, - # the pdb file gets overwritten. This doesn't happen on Unix because the shared library - # has a 'lib' prefix in the basename. - # @TODO Change the shared library name on Windows. - copydir( - "{build_dir}/shiboken2/libshiboken", - "{pyside_package_dir}/PySide2", - filter=pdbs, - recursive=False, vars=vars) - copydir( - "{build_dir}/pyside2/libpyside", - "{pyside_package_dir}/PySide2", - filter=pdbs, - recursive=False, vars=vars) + copydir( + "{build_dir}/shiboken2/libshiboken", + "{pyside_package_dir}/PySide2", + filter=pdbs, + recursive=False, vars=vars) + copydir( + "{build_dir}/pyside2/libpyside", + "{pyside_package_dir}/PySide2", + filter=pdbs, + recursive=False, vars=vars) def prepare_standalone_clang(self, is_win = False): """ Copies the libclang library to the pyside package so that shiboken exceutable works. """ @@ -1783,7 +1828,7 @@ if wheel_module_exists: setup( name = "PySide2", - version = __version__, + version = get_package_version(), description = ("Python bindings for the Qt cross-platform application and UI framework"), long_description = README + "\n\n" + CHANGES, classifiers = [ diff --git a/sources/pyside2/CMakeLists.txt b/sources/pyside2/CMakeLists.txt index aadefb818..4db611f0d 100644 --- a/sources/pyside2/CMakeLists.txt +++ b/sources/pyside2/CMakeLists.txt @@ -21,6 +21,27 @@ else() find_package(PythonLibs 2.6) endif() +set(PYSIDE_VERSION_FILE_PATH "${CMAKE_SOURCE_DIR}/pyside_version.py") +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS + ${PYSIDE_VERSION_FILE_PATH} +) +execute_process( + COMMAND ${PYTHON_EXECUTABLE} "${PYSIDE_VERSION_FILE_PATH}" + OUTPUT_VARIABLE PYSIDE_VERSION_OUTPUT + ERROR_VARIABLE PYSIDE_VERSION_OUTPUT_ERROR + OUTPUT_STRIP_TRAILING_WHITESPACE) +if (NOT PYSIDE_VERSION_OUTPUT) + message(FATAL_ERROR "Could not identify PySide2 version. Error: ${PYSIDE_VERSION_OUTPUT_ERROR}") +endif() + +list(GET PYSIDE_VERSION_OUTPUT 0 BINDING_API_MAJOR_VERSION) +list(GET PYSIDE_VERSION_OUTPUT 1 BINDING_API_MINOR_VERSION) +list(GET PYSIDE_VERSION_OUTPUT 2 BINDING_API_MICRO_VERSION) +# a - alpha, b - beta, rc - rc +list(GET PYSIDE_VERSION_OUTPUT 3 BINDING_API_PRE_RELEASE_VERSION_TYPE) +# the number of the pre release (alpha1, beta3, rc7, etc.) +list(GET PYSIDE_VERSION_OUTPUT 4 BINDING_API_PRE_RELEASE_VERSION) + macro(get_python_extension_suffix) # Result of imp.get_suffixes() depends on the platform, but generally looks something like: # [('.cpython-34m-x86_64-linux-gnu.so', 'rb', 3), ('.cpython-34m.so', 'rb', 3), @@ -161,19 +182,15 @@ if(NOT CMAKE_BUILD_TYPE) endif() set(BINDING_NAME PySide2) -set(BINDING_API_MAJOR_VERSION "2") -set(BINDING_API_MINOR_VERSION "0") -set(BINDING_API_MICRO_VERSION "0") -set(BINDING_API_RELEASE_LEVEL "alpha") # alpha, beta, rc, or final -set(BINDING_API_SERIAL 0) # leave as 0 when release level is final -set(BINDING_API_VERSION "${BINDING_API_MAJOR_VERSION}.${BINDING_API_MINOR_VERSION}.${BINDING_API_MICRO_VERSION}" CACHE STRING "PySide version" FORCE) + +set(BINDING_API_VERSION "${BINDING_API_MAJOR_VERSION}.${BINDING_API_MINOR_VERSION}.${BINDING_API_MICRO_VERSION}" CACHE STRING "PySide2 version" FORCE) set(PYSIDE_SO_VERSION ${BINDING_API_MAJOR_VERSION}.${BINDING_API_MINOR_VERSION}) -if (BINDING_API_RELEASE_LEVEL STREQUAL "final") +if (BINDING_API_PRE_RELEASE_VERSION_TYPE STREQUAL "") set(BINDING_API_VERSION_FULL "${BINDING_API_MAJOR_VERSION}.${BINDING_API_MINOR_VERSION}.${BINDING_API_MICRO_VERSION}" - CACHE STRING "PySide version [full]" FORCE) + CACHE STRING "PySide2 version [full]" FORCE) else() - set(BINDING_API_VERSION_FULL "${BINDING_API_MAJOR_VERSION}.${BINDING_API_MINOR_VERSION}.${BINDING_API_MICRO_VERSION}~${BINDING_API_RELEASE_LEVEL}${BINDING_API_SERIAL}" - CACHE STRING "PySide version [full]" FORCE) + set(BINDING_API_VERSION_FULL "${BINDING_API_MAJOR_VERSION}.${BINDING_API_MINOR_VERSION}.${BINDING_API_MICRO_VERSION}~${BINDING_API_PRE_RELEASE_VERSION_TYPE}${BINDING_API_PRE_RELEASE_VERSION}" + CACHE STRING "PySide2 version [full]" FORCE) endif() string(TIMESTAMP PYSIDE_BUILD_DATE "%Y-%m-%dT%H:%M:%S+00:00" UTC) @@ -181,6 +198,19 @@ if (PYSIDE_BUILD_DATE) set(PYSIDE_BUILD_DATE "__build_date__ = '${PYSIDE_BUILD_DATE}'") endif() +if (PYSIDE_SETUP_PY_PACKAGE_VERSION) + set(PYSIDE_SETUP_PY_PACKAGE_VERSION_ASSIGNMENT "__setup_py_package_version__ = '${PYSIDE_SETUP_PY_PACKAGE_VERSION}'") + set(FINAL_PACKAGE_VERSION ${PYSIDE_SETUP_PY_PACKAGE_VERSION}) +else() + set(FINAL_PACKAGE_VERSION ${BINDING_API_VERSION_FULL}) +endif() + +if (PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP) + set(PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT "__setup_py_package_timestamp__ = '${PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP}'") +else() + set(PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT "__setup_py_package_timestamp__ = ''") +endif() + find_package(Git) if(GIT_FOUND) # Check if current source folder is inside a git repo, so that commit information can be @@ -222,11 +252,6 @@ if(GIT_FOUND) endif() endif() -# Used by setup.py to know which symlink to resolve and copy in to the final package, in order to -# avoid resolving all symlinks and thus copying unnecessary duplicate files. -set(config_py_shiboken_library_version "") -set(config_py_pyside_library_version "") - include(PySideModules) macro(COLLECT_MODULE_IF_FOUND shortname) @@ -372,8 +397,6 @@ endif() # Define supported Qt Version set(SUPPORTED_QT_VERSION "${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}") -set(BINDING_VERSION ${BINDING_API_VERSION}.${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}) - # uninstall target configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" @@ -382,16 +405,6 @@ add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") -set(ARCHIVE_NAME pyside-qt${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}+${BINDING_API_VERSION_FULL}) -add_custom_target(dist - COMMAND mkdir -p "${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}" && - git log > "${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}/ChangeLog" && - git archive --prefix=${ARCHIVE_NAME}/ HEAD --format=tar --output="${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar" && - tar -C "${CMAKE_BINARY_DIR}" --owner=root --group=root -r "${ARCHIVE_NAME}/ChangeLog" -f "${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar" && - bzip2 -f9 "${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar" && - echo "Source package created at ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar.bz2.\n" - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - if (NOT PYTHON_SITE_PACKAGES) execute_process( COMMAND ${SHIBOKEN_PYTHON_INTERPRETER} -c "if True: @@ -424,6 +437,22 @@ else() message(STATUS "PySide will be generated using the protected hack!") endif() +# Build with Address sanitizer enabled if requested. This may break things, so use at your own risk. +if (SANITIZE_ADDRESS AND NOT MSVC) + # Currently this does not check that the clang / gcc version used supports Address sanitizer, + # so once again, use at your own risk. + add_compile_options("-fsanitize=address" "-g" "-fno-omit-frame-pointer") + # We need to add the sanitize address option to all linked executables / shared libraries + # so that proper sanitizer symbols are linked in. + # + # Note that when running tests, you may need to set an additional environment variable + # in set_tests_properties for shiboken2 / pyside tests, or exported in your shell. Address + # sanitizer will tell you what environment variable needs to be exported. For example: + # export DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/Toolchains/ + # ./XcodeDefault.xctoolchain/usr/lib/clang/8.1.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib + set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_STANDARD_LIBRARIES} -fsanitize=address") +endif() + add_subdirectory(libpyside) find_package(Qt5Designer) if(Qt5UiTools_FOUND AND Qt5Designer_FOUND) diff --git a/sources/pyside2/PySide2/CMakeLists.txt b/sources/pyside2/PySide2/CMakeLists.txt index 21db337e9..971d0a9ef 100644 --- a/sources/pyside2/PySide2/CMakeLists.txt +++ b/sources/pyside2/PySide2/CMakeLists.txt @@ -10,6 +10,9 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in" configure_file("${CMAKE_CURRENT_SOURCE_DIR}/_config.py.in" "${CMAKE_CURRENT_BINARY_DIR}/_config.py" @ONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/../pyside_version.py" + "${CMAKE_CURRENT_BINARY_DIR}/_git_pyside_version.py" @ONLY) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/__init__.py" "${CMAKE_CURRENT_BINARY_DIR}/support/__init__.py" COPYONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/__init__.py" @@ -69,6 +72,8 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" DESTINATION "${PYTHON_SITE_PACKAGES}/${BINDING_NAME}${pyside2_SUFFIX}") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_config.py" DESTINATION "${PYTHON_SITE_PACKAGES}/${BINDING_NAME}${pyside2_SUFFIX}") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_git_pyside_version.py" + DESTINATION "${PYTHON_SITE_PACKAGES}/${BINDING_NAME}${pyside2_SUFFIX}") install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/typesystem_templates.xml DESTINATION share/PySide2${pyside_SUFFIX}/typesystems) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pyside2_global.h diff --git a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml index 12b0f3115..0f6d48968 100644 --- a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml +++ b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml @@ -1731,6 +1731,9 @@ <modify-argument index="return"> <replace-type modified-type="QString"/> </modify-argument> + <inject-code class="native" position="end"> + <insert-template name="return_QString_native"/> + </inject-code> <inject-code class="target" position="end"> <insert-template name="return_QString"/> </inject-code> diff --git a/sources/pyside2/PySide2/__init__.py.in b/sources/pyside2/PySide2/__init__.py.in index 92e52a81a..4ce266b69 100644 --- a/sources/pyside2/PySide2/__init__.py.in +++ b/sources/pyside2/PySide2/__init__.py.in @@ -1,14 +1,17 @@ __all__ = list("Qt" + body for body in "@all_module_shortnames@" .split(";")) -__version__ = "@BINDING_API_VERSION_FULL@" -__version_info__ = (@BINDING_API_MAJOR_VERSION@, @BINDING_API_MINOR_VERSION@, @BINDING_API_MICRO_VERSION@, "@BINDING_API_RELEASE_LEVEL@", @BINDING_API_SERIAL@) +__version__ = "@FINAL_PACKAGE_VERSION@" +__version_info__ = (@BINDING_API_MAJOR_VERSION@, @BINDING_API_MINOR_VERSION@, @BINDING_API_MICRO_VERSION@, "@BINDING_API_PRE_RELEASE_VERSION_TYPE@", @BINDING_API_PRE_RELEASE_VERSION@) @PYSIDE_BUILD_DATE@ @PYSIDE_BUILD_COMMIT_DATE@ @PYSIDE_BUILD_COMMIT_HASH@ @PYSIDE_BUILD_COMMIT_HASH_DESCRIBED@ +# Timestamp used for snapshot build, which is part of snapshot package version. +@PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT@ + def _setupQtDirectories(): import sys import os diff --git a/sources/pyside2/PySide2/_config.py.in b/sources/pyside2/PySide2/_config.py.in index db8a17210..6f8d022dc 100644 --- a/sources/pyside2/PySide2/_config.py.in +++ b/sources/pyside2/PySide2/_config.py.in @@ -4,3 +4,14 @@ built_modules = list(name for name in shiboken_library_soversion = str(@SHIBOKEN_SO_VERSION@) pyside_library_soversion = str(@PYSIDE_SO_VERSION@) + +version = "@FINAL_PACKAGE_VERSION@" +version_info = (@BINDING_API_MAJOR_VERSION@, @BINDING_API_MINOR_VERSION@, @BINDING_API_MICRO_VERSION@, "@BINDING_API_PRE_RELEASE_VERSION_TYPE@", @BINDING_API_PRE_RELEASE_VERSION@) + +@PYSIDE_BUILD_DATE@ +@PYSIDE_BUILD_COMMIT_DATE@ +@PYSIDE_BUILD_COMMIT_HASH@ +@PYSIDE_BUILD_COMMIT_HASH_DESCRIBED@ + +# Timestamp used for snapshot build, which is part of snapshot package version. +@PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT@ diff --git a/sources/pyside2/PySide2/typesystem_templates.xml b/sources/pyside2/PySide2/typesystem_templates.xml index 1715c0253..11a384263 100644 --- a/sources/pyside2/PySide2/typesystem_templates.xml +++ b/sources/pyside2/PySide2/typesystem_templates.xml @@ -286,6 +286,12 @@ <template name="return_QString"> %PYARG_0 = %CONVERTTOPYTHON[QString](%1); </template> + <template name="return_QString_native"> + if (%ISCONVERTIBLE[QString](%PYARG_0)) + %1 = %CONVERTTOCPP[QString](%PYARG_0); + else + qWarning("%TYPE::%FUNCTION_NAME: Argument is not convertible to unicode."); + </template> <template name="return_tuple_QValidator_QString_int"> %BEGIN_ALLOW_THREADS diff --git a/sources/pyside2/pyside_version.py b/sources/pyside2/pyside_version.py new file mode 100644 index 000000000..aacb7e0d9 --- /dev/null +++ b/sources/pyside2/pyside_version.py @@ -0,0 +1,49 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of PySide2. +## +## $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$ +## +############################################################################# + +major_version = "5" +minor_version = "9" +patch_version = "0" +pre_release_version_type = "a" # e.g. "a", "b", "rc". +pre_release_version = "1" # e.g "1", "2", (which means "beta1", "beta2", if type is "b") + +if __name__ == '__main__': + # Used by CMake. + print('{0};{1};{2};{3};{4}'.format(major_version, minor_version, patch_version, + pre_release_version_type, pre_release_version)) diff --git a/sources/pyside2/tests/QtWidgets/qvalidator_test.py b/sources/pyside2/tests/QtWidgets/qvalidator_test.py index 951d6b2b0..dd5eaadb3 100644 --- a/sources/pyside2/tests/QtWidgets/qvalidator_test.py +++ b/sources/pyside2/tests/QtWidgets/qvalidator_test.py @@ -29,38 +29,49 @@ from PySide2.QtCore import * from PySide2.QtGui import * from PySide2.QtWidgets import * +from PySide2.QtTest import * import unittest from helper import UsesQApplication class MyValidator1(QValidator): - def fixUp(self, input): + def fixup(self, input): return "fixed" def validate(self, input, pos): return (QValidator.Acceptable, "fixed", 1) class MyValidator2(QValidator): - def fixUp(self, input): + def fixup(self, input): return "fixed" def validate(self, input, pos): return (QValidator.Acceptable, "fixed") class MyValidator3(QValidator): - def fixUp(self, input): + def fixup(self, input): return "fixed" def validate(self, input, pos): return (QValidator.Acceptable,) class MyValidator4(QValidator): - def fixUp(self, input): + def fixup(self, input): return "fixed" def validate(self, input, pos): return QValidator.Acceptable +class MyValidator5(QValidator): + def validate(self, input, pos): + if input.islower(): + return (QValidator.Intermediate, input, pos) + else: + return (QValidator.Acceptable, input, pos) + + def fixup(self, input): + return "22" + class QValidatorTest(UsesQApplication): def testValidator1(self): line = QLineEdit() @@ -110,5 +121,13 @@ class QValidatorTest(UsesQApplication): self.assertEqual(line.text(), "foo") self.assertEqual(line.cursorPosition(), 3) + def testValidator5(self): + line = QLineEdit() + line.show() + line.setValidator(MyValidator5()) + line.setText("foo") + QTest.keyClick(line, Qt.Key_Return) + self.assertEqual(line.text(), "22") + if __name__ == '__main__': unittest.main() diff --git a/sources/pyside2/tests/pysidetest/version_test.py b/sources/pyside2/tests/pysidetest/version_test.py index 5901a56c1..01e88dbc6 100644 --- a/sources/pyside2/tests/pysidetest/version_test.py +++ b/sources/pyside2/tests/pysidetest/version_test.py @@ -33,8 +33,10 @@ from PySide2 import __version_info__, __version__, QtCore class CheckForVariablesTest(unittest.TestCase): def testVesions(self): - self.assertTrue(__version_info__ >= (1, 0, 0)) - self.assertTrue(__version_info__ < (99, 99, 99)) + version_tuple = (__version_info__[0], __version_info__[1], __version_info__[2]) + self.assertTrue(version_tuple >= (1, 0, 0)) + + self.assertTrue(version_tuple < (99, 99, 99)) self.assertTrue(__version__) self.assertTrue(QtCore.__version_info__ >= (4, 5, 0)) diff --git a/sources/shiboken2/CMakeLists.txt b/sources/shiboken2/CMakeLists.txt index 006924ad9..ccabc72e3 100644 --- a/sources/shiboken2/CMakeLists.txt +++ b/sources/shiboken2/CMakeLists.txt @@ -11,12 +11,6 @@ find_package(Qt5 REQUIRED COMPONENTS Core Xml XmlPatterns) add_definitions(${Qt5Core_DEFINITIONS}) -set(shiboken_MAJOR_VERSION "2") -set(shiboken_MINOR_VERSION "0") -set(shiboken_MICRO_VERSION "0") -set(shiboken2_VERSION "${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}.${shiboken_MICRO_VERSION}") -set(shiboken2_library_so_version "${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}") - option(BUILD_TESTS "Build tests." TRUE) option(USE_PYTHON_VERSION "Use specific python version to build shiboken2." "") @@ -126,6 +120,31 @@ message(STATUS "CLANG builtins includes directory chosen: ${CLANG_BUILTIN_INCLUD set(CLANG_EXTRA_INCLUDES ${CLANG_DIR}/include) set(CLANG_EXTRA_LIBRARIES ${CLANG_LIBRARY}) +set(SHIBOKEN_VERSION_FILE_PATH "${CMAKE_SOURCE_DIR}/shiboken_version.py") +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS + ${SHIBOKEN_VERSION_FILE_PATH} +) +execute_process( + COMMAND ${PYTHON_EXECUTABLE} "${SHIBOKEN_VERSION_FILE_PATH}" + OUTPUT_VARIABLE SHIBOKEN_VERSION_OUTPUT + ERROR_VARIABLE SHIBOKEN_VERSION_OUTPUT_ERROR + OUTPUT_STRIP_TRAILING_WHITESPACE) +if (NOT SHIBOKEN_VERSION_OUTPUT) + message(FATAL_ERROR "Could not identify shiboken version. \ + Error: ${SHIBOKEN_VERSION_OUTPUT_ERROR}") +endif() + +list(GET SHIBOKEN_VERSION_OUTPUT 0 shiboken_MAJOR_VERSION) +list(GET SHIBOKEN_VERSION_OUTPUT 1 shiboken_MINOR_VERSION) +list(GET SHIBOKEN_VERSION_OUTPUT 2 shiboken_MICRO_VERSION) +# a - alpha, b - beta, rc - rc +list(GET SHIBOKEN_VERSION_OUTPUT 3 shiboken_PRE_RELEASE_VERSION_TYPE) +# the number of the pre release (alpha1, beta3, rc7, etc.) +list(GET SHIBOKEN_VERSION_OUTPUT 4 shiboken_PRE_RELEASE_VERSION) + +set(shiboken2_VERSION "${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}.${shiboken_MICRO_VERSION}") +set(shiboken2_library_so_version "${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}") + ## For debugging the PYTHON* variables message("PYTHONLIBS_FOUND: " ${PYTHONLIBS_FOUND}) message("PYTHON_LIBRARIES: " ${PYTHON_LIBRARIES}) @@ -247,6 +266,22 @@ if(CMAKE_HOST_APPLE) endif() endif() +# Build with Address sanitizer enabled if requested. This may break things, so use at your own risk. +if (SANITIZE_ADDRESS AND NOT MSVC) + # Currently this does not check that the clang / gcc version used supports Address sanitizer, + # so once again, use at your own risk. + add_compile_options("-fsanitize=address" "-g" "-fno-omit-frame-pointer") + # We need to add the sanitize address option to all linked executables / shared libraries + # so that proper sanitizer symbols are linked in. + # + # Note that when running tests, you may need to set an additional environment variable + # in set_tests_properties for shiboken2 / pyside tests, or exported in your shell. Address + # sanitizer will tell you what environment variable needs to be exported. For example: + # export DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/Toolchains/ + # ./XcodeDefault.xctoolchain/usr/lib/clang/8.1.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib + set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_STANDARD_LIBRARIES} -fsanitize=address") +endif() + add_subdirectory(ApiExtractor) set(generator_plugin_DIR ${LIB_INSTALL_DIR}/generatorrunner${generator_SUFFIX}) @@ -366,16 +401,3 @@ else() endif() add_subdirectory(data) - -# dist target -set(ARCHIVE_NAME ${CMAKE_PROJECT_NAME}-${shiboken2_VERSION}) -add_custom_target(dist - COMMAND mkdir -p "${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}" && - git log > "${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}/ChangeLog" && - git archive --prefix=${ARCHIVE_NAME}/ HEAD --format=tar --output="${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar" && - tar -C "${CMAKE_BINARY_DIR}" --owner=root --group=root -r "${ARCHIVE_NAME}/ChangeLog" -f "${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar" && - bzip2 -f9 "${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar" && - echo "Source package created at ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar.bz2." - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - - diff --git a/sources/shiboken2/shiboken_version.py b/sources/shiboken2/shiboken_version.py new file mode 100644 index 000000000..aacb7e0d9 --- /dev/null +++ b/sources/shiboken2/shiboken_version.py @@ -0,0 +1,49 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of PySide2. +## +## $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$ +## +############################################################################# + +major_version = "5" +minor_version = "9" +patch_version = "0" +pre_release_version_type = "a" # e.g. "a", "b", "rc". +pre_release_version = "1" # e.g "1", "2", (which means "beta1", "beta2", if type is "b") + +if __name__ == '__main__': + # Used by CMake. + print('{0};{1};{2};{3};{4}'.format(major_version, minor_version, patch_version, + pre_release_version_type, pre_release_version)) @@ -323,7 +323,7 @@ def makefile(dst, content=None, vars=None): def copydir(src, dst, filter=None, ignore=None, force=True, recursive=True, vars=None, - dir_filter_function=None, force_copy_symlinks=False): + dir_filter_function=None, file_filter_function=None, force_copy_symlinks=False): if vars is not None: src = src.format(**vars) @@ -357,10 +357,12 @@ def copydir(src, dst, filter=None, ignore=None, force=True, recursive=True, vars if recursive: results.extend( copydir(srcname, dstname, filter, ignore, force, recursive, - vars, dir_filter_function, force_copy_symlinks)) + vars, dir_filter_function, file_filter_function, + force_copy_symlinks)) else: - if (filter is not None and not filter_match(name, filter)) or \ - (ignore is not None and filter_match(name, ignore)): + if (file_filter_function is not None and not file_filter_function(name, srcname)) \ + or (filter is not None and not filter_match(name, filter)) \ + or (ignore is not None and filter_match(name, ignore)): continue if not os.path.exists(dst): os.makedirs(dst) @@ -983,3 +985,28 @@ def rpathsHasOrigin(rpaths): if match: return True return False + +def memoize(function): + """ Decorator to wrap a function with a memoizing callable. + It returns cached values when the wrapped function is called with the same arguments. + """ + memo = {} + def wrapper(*args): + if args in memo: + return memo[args] + else: + rv = function(*args) + memo[args] = rv + return rv + return wrapper + +def get_python_dict(python_script_path): + try: + with open(python_script_path) as f: + python_dict = {} + code = compile(f.read(), python_script_path, 'exec') + exec(code, {}, python_dict) + return python_dict + except IOError as e: + print("get_python_dict: Couldn't get dict from python file: {}.".format(python_script_path)) + raise |