aboutsummaryrefslogtreecommitdiffstats
path: root/build_scripts/setup_runner.py
diff options
context:
space:
mode:
Diffstat (limited to 'build_scripts/setup_runner.py')
-rw-r--r--build_scripts/setup_runner.py252
1 files changed, 182 insertions, 70 deletions
diff --git a/build_scripts/setup_runner.py b/build_scripts/setup_runner.py
index 1a7317e4d..5d0466247 100644
--- a/build_scripts/setup_runner.py
+++ b/build_scripts/setup_runner.py
@@ -1,53 +1,21 @@
-#############################################################################
-##
-## 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$
-##
-#############################################################################
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-import sys
import os
+import sys
+import tempfile
import textwrap
+import logging
+
+from pathlib import Path
+from setuptools import setup
from build_scripts.config import config
-from build_scripts.main import get_package_version, get_setuptools_extension_modules
-from build_scripts.main import cmd_class_dict
-from build_scripts.options import OPTION
+from build_scripts.main import (cmd_class_dict, get_package_version,
+ get_setuptools_extension_modules)
+from build_scripts.options import ADDITIONAL_OPTIONS, OPTION
from build_scripts.utils import run_process
-
-from setuptools import setup
+from build_scripts.log import log, LogLevel
class SetupRunner(object):
@@ -59,38 +27,140 @@ class SetupRunner(object):
self.orig_argv = orig_argv
self.sub_argv = list(orig_argv)
- self.setup_script_dir = os.getcwd()
+ self.setup_script_dir = Path.cwd()
@staticmethod
def cmd_line_argument_is_in_args(argument, args):
""" Check if command line argument was passed in args. """
- return any(arg for arg in list(args) if "--" + argument in arg)
+ return any(arg for arg in list(args) if f"--{argument}" in arg)
+
+ @staticmethod
+ def get_cmd_line_argument_in_args(argument, args):
+ """ Gets the value of a cmd line argument passed in args. """
+ for arg in list(args):
+ if f"--{argument}" in arg:
+ prefix = f"--{argument}"
+ prefix_len = len(prefix) + 1
+ return arg[prefix_len:]
+ return None
@staticmethod
def remove_cmd_line_argument_in_args(argument, args):
""" Remove command line argument from args. """
- return [arg for arg in list(args) if "--" + argument not in arg]
+ return [arg for arg in list(args) if f"--{argument}" not in arg]
@staticmethod
def construct_cmd_line_argument(name, value=None):
""" Constructs a command line argument given name and value. """
if not value:
- return "--{}".format(name)
- return "--{}={}".format(name, value)
+ return f"--{name}"
+ return f"--{name}={value}"
@staticmethod
def construct_internal_build_type_cmd_line_argument(internal_build_type):
return SetupRunner.construct_cmd_line_argument("internal-build-type", internal_build_type)
- def add_setup_internal_invocation(self, build_type, reuse_build=False):
- """ Enqueues a script sub-invocation to be executed later. """
+ def enqueue_setup_internal_invocation(self, setup_cmd):
+ self.invocations_list.append(setup_cmd)
+
+ def add_setup_internal_invocation(self, build_type, reuse_build=False, extra_args=None):
+ setup_cmd = self.new_setup_internal_invocation(build_type, reuse_build, extra_args)
+ self.enqueue_setup_internal_invocation(setup_cmd)
+
+ def new_setup_internal_invocation(self, build_type,
+ reuse_build=False,
+ extra_args=None,
+ replace_command_with=None):
+ """ Creates a script sub-invocation to be executed later. """
internal_build_type_arg = self.construct_internal_build_type_cmd_line_argument(build_type)
- setup_cmd = [sys.executable] + self.sub_argv + [internal_build_type_arg]
+
+ command_index = 0
+ command = self.sub_argv[command_index]
+ if command == 'setup.py' and len(self.sub_argv) > 1:
+ command_index = 1
+ command = self.sub_argv[command_index]
+
+ # Make a copy
+ modified_argv = list(self.sub_argv)
+
+ if replace_command_with:
+ modified_argv[command_index] = replace_command_with
+
+ setup_cmd = [sys.executable] + modified_argv + [internal_build_type_arg]
+
+ if extra_args:
+ for (name, value) in extra_args:
+ setup_cmd.append(self.construct_cmd_line_argument(name, value))
# Add --reuse-build option if requested and not already present.
- if reuse_build and not self.cmd_line_argument_is_in_args("reuse-build", self.sub_argv):
+ if (reuse_build and command in ('bdist_wheel', 'build', 'build_base_docs', 'install')
+ and not self.cmd_line_argument_is_in_args("reuse-build", modified_argv)):
setup_cmd.append(self.construct_cmd_line_argument("reuse-build"))
- self.invocations_list.append(setup_cmd)
+ return setup_cmd
+
+ def add_host_tools_setup_internal_invocation(self, initialized_config):
+ extra_args = []
+ extra_host_args = []
+
+ # When cross-compiling, build the host shiboken generator tool
+ # only if a path to an existing one was not provided.
+ if not self.cmd_line_argument_is_in_args("shiboken-host-path", self.sub_argv):
+ handle, initialized_config.shiboken_host_query_path = tempfile.mkstemp()
+ os.close(handle)
+
+ # Tell the setup process to create a file with the location
+ # of the installed host shiboken as its contents.
+ extra_host_args.append(
+ ("internal-cmake-install-dir-query-file-path",
+ initialized_config.shiboken_host_query_path))
+
+ # Tell the other setup invocations to read that file and use
+ # the read path as the location of the host shiboken.
+ extra_args.append(
+ ("internal-shiboken-host-path-query-file",
+ initialized_config.shiboken_host_query_path)
+ )
+
+ # This is specifying shiboken_module_option_name
+ # instead of shiboken_generator_option_name, but it will
+ # actually build the generator.
+ host_cmd = self.new_setup_internal_invocation(
+ initialized_config.shiboken_module_option_name,
+ extra_args=extra_host_args,
+ replace_command_with="build")
+
+ # To build the host tools, we reuse the initial target
+ # command line arguments, but we remove some options that
+ # don't make sense for the host build.
+
+ # Drop the toolchain arg.
+ host_cmd = self.remove_cmd_line_argument_in_args("cmake-toolchain-file",
+ host_cmd)
+
+ # Drop the target plat-name arg if there is one.
+ if self.cmd_line_argument_is_in_args("plat-name", host_cmd):
+ host_cmd = self.remove_cmd_line_argument_in_args("plat-name", host_cmd)
+
+ # Drop the python-target-path arg if there is one.
+ if self.cmd_line_argument_is_in_args("python-target-path", host_cmd):
+ host_cmd = self.remove_cmd_line_argument_in_args("python-target-path", host_cmd)
+
+ # Drop the target build-tests arg if there is one.
+ if self.cmd_line_argument_is_in_args("build-tests", host_cmd):
+ host_cmd = self.remove_cmd_line_argument_in_args("build-tests", host_cmd)
+
+ # Make sure to pass the qt host path as the target path
+ # when doing the host build. And make sure to remove any
+ # existing qt target path.
+ if self.cmd_line_argument_is_in_args("qt-host-path", host_cmd):
+ qt_host_path = self.get_cmd_line_argument_in_args("qt-host-path", host_cmd)
+ host_cmd = self.remove_cmd_line_argument_in_args("qt-host-path", host_cmd)
+ host_cmd = self.remove_cmd_line_argument_in_args("qt-target-path", host_cmd)
+ host_cmd.append(self.construct_cmd_line_argument("qt-target-path",
+ qt_host_path))
+
+ self.enqueue_setup_internal_invocation(host_cmd)
+ return extra_args
def run_setup(self):
"""
@@ -101,6 +171,13 @@ class SetupRunner(object):
will run setuptools.setup().
"""
+ # PYSIDE-1746: We prevent the generation of .pyc/.pyo files during installation.
+ # These files are generated anyway on their import.
+ sys.dont_write_bytecode = True
+ qt_install_path = OPTION["QTPATHS"]
+ if qt_install_path:
+ qt_install_path = Path(qt_install_path).parents[1]
+
# Prepare initial config.
config.init_config(build_type=OPTION["BUILD_TYPE"],
internal_build_type=OPTION["INTERNAL_BUILD_TYPE"],
@@ -108,14 +185,25 @@ class SetupRunner(object):
package_version=get_package_version(),
ext_modules=get_setuptools_extension_modules(),
setup_script_dir=self.setup_script_dir,
- quiet=OPTION["QUIET"])
+ cmake_toolchain_file=OPTION["CMAKE_TOOLCHAIN_FILE"],
+ log_level=OPTION["LOG_LEVEL"],
+ qt_install_path=qt_install_path)
+
+ # Enable logging for both the top-level invocation of setup.py
+ # as well as for child invocations. We we now use
+ if OPTION["LOG_LEVEL"] == LogLevel.VERBOSE:
+ log.setLevel(logging.DEBUG)
+ elif OPTION["LOG_LEVEL"] == LogLevel.QUIET:
+ log.setLevel(logging.ERROR)
+ elif OPTION["LOG_LEVEL"] == LogLevel.INFO:
+ log.setLevel(logging.INFO)
# This is an internal invocation of setup.py, so start actual
# build.
if config.is_internal_invocation():
if config.internal_build_type not in config.get_allowed_internal_build_values():
- raise RuntimeError("Invalid '{}' option given to --internal-build-type. "
- .format(config.internal_build_type))
+ raise RuntimeError(f"Invalid '{config.internal_build_type}' option given to "
+ "--internal-build-type. ")
self.run_setuptools_setup()
return
@@ -123,19 +211,37 @@ class SetupRunner(object):
# modules we will build and depending on that, call setup.py
# multiple times with different arguments.
if config.build_type not in config.get_allowed_top_level_build_values():
- raise RuntimeError("Invalid '{}' option given to --build-type. "
- .format(config.build_type))
+ raise RuntimeError(f"Invalid '{config.build_type}' option given to --build-type. ")
- # Build everything: shiboken2, shiboken2-generator and PySide2.
- if config.is_top_level_build_all():
- self.add_setup_internal_invocation(config.shiboken_module_option_name)
+ # Build everything: shiboken6, shiboken6-generator and PySide6.
+ help_requested = '--help' in self.sub_argv or '-h' in self.sub_argv
+
+ if help_requested:
+ self.add_setup_internal_invocation(config.pyside_option_name)
+
+ elif config.is_top_level_build_all():
+ extra_args = []
+
+ # extra_args might contain the location of the built host
+ # shiboken, which needs to be passed to the other
+ # target invocations.
+ if config.is_cross_compile():
+ extra_args = self.add_host_tools_setup_internal_invocation(config)
+
+ self.add_setup_internal_invocation(
+ config.shiboken_module_option_name,
+ extra_args=extra_args)
# Reuse the shiboken build for the generator package instead
# of rebuilding it again.
- self.add_setup_internal_invocation(config.shiboken_generator_option_name,
- reuse_build=True)
+ # Don't build it in a cross-build though.
+ if not config.is_cross_compile():
+ self.add_setup_internal_invocation(
+ config.shiboken_generator_option_name,
+ reuse_build=True)
- self.add_setup_internal_invocation(config.pyside_option_name)
+ self.add_setup_internal_invocation(config.pyside_option_name,
+ extra_args=extra_args)
elif config.is_top_level_build_shiboken_module():
self.add_setup_internal_invocation(config.shiboken_module_option_name)
@@ -148,15 +254,21 @@ class SetupRunner(object):
for cmd in self.invocations_list:
cmd_as_string = " ".join(cmd)
- print("\nRunning process: {}\n".format(cmd_as_string))
exit_code = run_process(cmd)
if exit_code != 0:
- msg = textwrap.dedent("""
- setup.py invocation failed with exit code: {}.\n\n
- setup.py invocation was: {}
- """).format(exit_code, cmd_as_string)
+ msg = textwrap.dedent(f"""
+ setup.py invocation failed with exit code: {exit_code}.\n\n
+ setup.py invocation was: {cmd_as_string}
+ """)
raise RuntimeError(msg)
+ if help_requested:
+ print(ADDITIONAL_OPTIONS)
+
+ # Cleanup temp query file.
+ if config.shiboken_host_query_path:
+ os.remove(config.shiboken_host_query_path)
+
@staticmethod
def run_setuptools_setup():
"""