diff options
Diffstat (limited to 'testing')
-rw-r--r-- | testing/wheel_tester.py | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/testing/wheel_tester.py b/testing/wheel_tester.py new file mode 100644 index 000000000..60fd7a38a --- /dev/null +++ b/testing/wheel_tester.py @@ -0,0 +1,295 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +""" +This script is used by Coin (coin_test_instructions.py specifically) to +test installation of generated wheels, and test building of the +"buildable" examples samplebinding and scriptableapplication. + +It can also be invoked regularly from the command line via +python testing/wheel_tester.py --qmake=some-value --cmake=some-value + +The qmake and cmake arguments can also be omitted, and they will be +looked up in your PATH. + +Make sure that some generated wheels already exist in the dist/ +directory (e.g. setup.py bdist_wheel was already executed). +""" + +import os, sys + +try: + this_file = __file__ +except NameError: + this_file = sys.argv[0] +this_file = os.path.abspath(this_file) +this_dir = os.path.dirname(this_file) +setup_script_dir = os.path.abspath(os.path.join(this_dir, '..')) +sys.path.append(setup_script_dir) + +from build_scripts.options import OPTION_QMAKE +from build_scripts.options import OPTION_CMAKE + +from build_scripts.utils import find_files_using_glob +from build_scripts.utils import find_glob_in_path +from build_scripts.utils import run_process +from build_scripts.utils import rmtree +import distutils.log as log + +log.set_verbosity(1) + + +def find_executable_qmake(): + return find_executable('qmake', OPTION_QMAKE) + + +def find_executable_cmake(): + return find_executable('cmake', OPTION_CMAKE) + + +def find_executable(executable, command_line_value): + value = command_line_value + option_str = '--{}'.format(executable) + + if value: + log.info("{} option given: {}".format(option_str, value)) + if not os.path.exists(value): + raise RuntimeError("No executable exists at: {}".format(value)) + else: + log.info("No {} option given, trying to find {} in PATH.".format(option_str, executable)) + paths = find_glob_in_path(executable) + log.info("{} executables found in PATH: {}".format(executable, paths)) + if not paths: + raise RuntimeError( + "No {} option was specified and no {} was found " + "in PATH.".format(option_str, executable)) + else: + value = paths[0] + log.info("Using {} found in PATH: {}".format(executable, value)) + log.info("") + return value + + +QMAKE_PATH = find_executable_qmake() +CMAKE_PATH = find_executable_cmake() + + +def get_wheels_dir(): + return os.path.join(setup_script_dir, "dist") + + +def get_examples_dir(): + return os.path.join(setup_script_dir, "examples") + + +def package_prefix_names(): + return ["shiboken2", "shiboken2_generator", "PySide2"] + + +def clean_egg_info(): + # After a successful bdist_wheel build, some .egg-info directories + # are left over, which confuse pip when invoking it via + # python -m pip, making pip think that the packages are already + # installed in the root source directory. + # Clean up the .egg-info directories to fix this, it should be + # safe to do so. + paths = find_files_using_glob(setup_script_dir, "*.egg-info") + for p in paths: + log.info("Removing {}".format(p)) + rmtree(p) + + +def install_wheel(wheel_path): + log.info("Installing wheel: {}".format(wheel_path)) + exit_code = run_process([sys.executable, "-m", "pip", "install", wheel_path]) + log.info("") + if exit_code: + raise RuntimeError("Error while installing wheel {}".format(wheel_path)) + + +def try_install_wheels(wheels_dir, py_version): + clean_egg_info() + all_wheels_pattern = "*.whl" + all_wheels = find_files_using_glob(wheels_dir, all_wheels_pattern) + + if len(all_wheels) > 1: + log.info("Found the following wheels in {}: ".format(wheels_dir)) + for wheel in all_wheels: + log.info(wheel) + else: + log.info("No wheels found in {}".format(wheels_dir)) + log.info("") + + for p in package_prefix_names(): + pattern = "{}-*cp{}*.whl".format(p, py_version) + files = find_files_using_glob(wheels_dir, pattern) + if files and len(files) == 1: + wheel_path = files[0] + install_wheel(wheel_path) + elif len(files) > 1: + raise RuntimeError("More than one wheel found for specific package and version.") + else: + raise RuntimeError("No wheels compatible with Python {} found " + "for testing.".format(py_version)) + + +def is_unix(): + if sys.platform.startswith("linux") or sys.platform == "darwin": + return True + return False + + +def generate_build_cmake(): + args = [CMAKE_PATH] + if is_unix(): + args.extend(["-G", "Unix Makefiles"]) + else: + args.extend(["-G", "NMake Makefiles"]) + args.append("-DCMAKE_BUILD_TYPE=Release") + args.append("-Dpython_interpreter={}".format(sys.executable)) + + # Specify prefix path so find_package(Qt5) works. + qmake_dir = os.path.abspath(os.path.join(os.path.dirname(QMAKE_PATH), "..")) + args.append("-DCMAKE_PREFIX_PATH={}".format(qmake_dir)) + + args.append("..") + + exit_code = run_process(args) + if exit_code: + raise RuntimeError("Failure while running cmake.") + log.info("") + + +def generate_build_qmake(): + exit_code = run_process([QMAKE_PATH, "..", "python_interpreter={}".format(sys.executable)]) + if exit_code: + raise RuntimeError("Failure while running qmake.") + log.info("") + + +def run_make(): + args = [] + if is_unix(): + executable = "make" + else: + executable = "nmake" + args.append(executable) + + exit_code = run_process(args) + if exit_code: + raise RuntimeError("Failure while running {}.".format(executable)) + log.info("") + + +def run_make_install(): + args = [] + if is_unix(): + executable = "make" + else: + executable = "nmake" + args.append(executable) + args.append("install") + + exit_code = run_process(args) + if exit_code: + raise RuntimeError("Failed while running {} install.".format(executable)) + log.info("") + + +def execute_script(script_path): + args = [sys.executable, script_path] + exit_code = run_process(args) + if exit_code: + raise RuntimeError("Failure while executing script: {}".format(script_path)) + log.info("") + + +def prepare_build_folder(src_path, build_folder_name): + build_path = os.path.join(src_path, build_folder_name) + + # The script can be called for both Python 2 and Python 3 wheels, so + # preparing a build folder should clean any previous existing build. + if os.path.exists(build_path): + log.info("Removing {}".format(build_path)) + rmtree(build_path) + + log.info("Creating {}".format(build_path)) + os.makedirs(build_path) + os.chdir(build_path) + + +def try_build_examples(): + examples_dir = get_examples_dir() + + log.info("Attempting to build and run samplebinding using cmake.") + src_path = os.path.join(examples_dir, "samplebinding") + prepare_build_folder(src_path, "cmake") + generate_build_cmake() + run_make() + run_make_install() + execute_script(os.path.join(src_path, "main.py")) + + log.info("Attempting to build scriptableapplication using cmake.") + src_path = os.path.join(examples_dir, "scriptableapplication") + prepare_build_folder(src_path, "cmake") + generate_build_cmake() + run_make() + + log.info("Attempting to build scriptableapplication using qmake.") + src_path = os.path.join(examples_dir, "scriptableapplication") + prepare_build_folder(src_path, "qmake") + generate_build_qmake() + run_make() + + +def run_wheel_tests(): + wheels_dir = get_wheels_dir() + py_version = sys.version_info[0] + + log.info("Attempting to install wheels.\n") + try_install_wheels(wheels_dir, py_version) + + log.info("Attempting to build examples.\n") + try_build_examples() + + log.info("All tests passed!") + + +if __name__ == "__main__": + run_wheel_tests() |