aboutsummaryrefslogtreecommitdiffstats
path: root/build_scripts/options.py
diff options
context:
space:
mode:
Diffstat (limited to 'build_scripts/options.py')
-rw-r--r--build_scripts/options.py217
1 files changed, 183 insertions, 34 deletions
diff --git a/build_scripts/options.py b/build_scripts/options.py
index 045eb05d0..6ce53b982 100644
--- a/build_scripts/options.py
+++ b/build_scripts/options.py
@@ -39,11 +39,13 @@
try:
from setuptools._distutils import log
+ from setuptools import Command
except ModuleNotFoundError:
# This is motivated by our CI using an old version of setuptools
# so then the coin_build_instructions.py script is executed, and
# import from this file, it was failing.
from distutils import log
+ from distutils.cmd import Command
from shutil import which
import sys
import os
@@ -51,6 +53,7 @@ import warnings
from pathlib import Path
from .qtinfo import QtInfo
+from .utils import memoize
_AVAILABLE_MKSPECS = ["ninja", "msvc", "mingw"] if sys.platform == "win32" else ["ninja", "make"]
@@ -63,6 +66,9 @@ Additional options:
---macos-use-libc++ Use libc++ on macOS
--snapshot-build Snapshot build
--package-timestamp Package Timestamp
+ --cmake-toolchain-file Path to CMake toolchain to enable cross-compiling
+ --shiboken-host-path Path to host shiboken package when cross-compiling
+ --qt-host-path Path to host Qt installation when cross-compiling
"""
@@ -164,7 +170,7 @@ def _jobs_option_value():
# Declare options which need to be known when instantiating the DistUtils
-# commands.
+# commands or even earlier during SetupRunner.run().
OPTION = {
"BUILD_TYPE": option_value("build-type"),
"INTERNAL_BUILD_TYPE": option_value("internal-build-type"),
@@ -179,7 +185,11 @@ OPTION = {
"PACKAGE_TIMESTAMP": option_value("package-timestamp"),
# This is used automatically by distutils.command.install object, to
# specify the final installation location.
- "FINAL_INSTALL_PREFIX": option_value("prefix", remove=False)
+ "FINAL_INSTALL_PREFIX": option_value("prefix", remove=False),
+ "CMAKE_TOOLCHAIN_FILE": option_value("cmake-toolchain-file"),
+ "SHIBOKEN_HOST_PATH": option_value("shiboken-host-path"),
+ "SHIBOKEN_HOST_PATH_QUERY_FILE": option_value("internal-shiboken-host-path-query-file"),
+ "QT_HOST_PATH": option_value("qt-host-path")
# This is used to identify the template for doc builds
}
_deprecated_option_jobs = option_value('jobs')
@@ -191,7 +201,7 @@ if _deprecated_option_jobs:
class DistUtilsCommandMixin(object):
"""Mixin for the DistUtils build/install commands handling the options."""
- _finalized = False
+ _static_class_finalized_once = False
mixin_user_options = [
('avoid-protected-hack', None, 'Force --avoid-protected-hack'),
@@ -217,9 +227,16 @@ class DistUtilsCommandMixin(object):
('qtpaths=', None, 'Path to qtpaths'),
('qmake=', None, 'Path to qmake (deprecated, use qtpaths)'),
('qt=', None, 'Qt version'),
+ ('qt-target-path=', None,
+ 'Path to device Qt installation (use Qt libs when cross-compiling)'),
('cmake=', None, 'Path to CMake'),
('openssl=', None, 'Path to OpenSSL libraries'),
+
+ # FIXME: Deprecated in favor of shiboken-target-path
('shiboken-config-dir=', None, 'shiboken configuration directory'),
+
+ ('shiboken-target-path=', None, 'Path to target shiboken package'),
+ ('python-target-path=', None, 'Path to target Python installation / prefix'),
('make-spec=', None, 'Qt make-spec'),
('macos-arch=', None, 'macOS architecture'),
('macos-sysroot=', None, 'macOS sysroot'),
@@ -230,7 +247,13 @@ class DistUtilsCommandMixin(object):
('qt-conf-prefix=', None, 'Qt configuration prefix'),
('qt-src-dir=', None, 'Qt source directory'),
('no-qt-tools', None, 'Do not copy the Qt tools'),
- ('pyside-numpy-support', None, 'libpyside: Add (experimental) numpy support')
+ ('pyside-numpy-support', None, 'libpyside: Add (experimental) numpy support'),
+ ('internal-cmake-install-dir-query-file-path=', None,
+ 'Path to file where the CMake install path of the project will be saved'),
+
+ # We redeclare plat-name as an option so it's recognized by the
+ # install command and doesn't throw an error.
+ ('plat-name=', None, 'The platform name for which we are cross-compiling'),
]
def __init__(self):
@@ -259,9 +282,17 @@ class DistUtilsCommandMixin(object):
self.qmake = None
self.has_qmake_option = False
self.qt = '5'
+ self.qt_host_path = None
+ self.qt_target_path = None
self.cmake = None
self.openssl = None
self.shiboken_config_dir = None
+ self.shiboken_host_path = None
+ self.shiboken_host_path_query_file = None
+ self.shiboken_target_path = None
+ self.python_target_path = None
+ self.is_cross_compile = False
+ self.cmake_toolchain_file = None
self.make_spec = None
self.macos_arch = None
self.macos_sysroot = None
@@ -273,16 +304,62 @@ class DistUtilsCommandMixin(object):
self.qt_src_dir = None
self.no_qt_tools = False
self.pyside_numpy_support = False
+ self.plat_name = None
+ self.internal_cmake_install_dir_query_file_path = None
+ self._per_command_mixin_options_finalized = False
+
+ # When initializing a command other than the main one (so the
+ # first one), we need to copy the user options from the main
+ # command to the new command options dict. Then
+ # Distribution.get_command_obj will pick up the copied options
+ # ensuring that all commands that inherit from
+ # the mixin, get our custom properties set by the time
+ # finalize_options is called.
+ if DistUtilsCommandMixin._static_class_finalized_once:
+ current_command: Command = self
+ dist = current_command.distribution
+ main_command_name = dist.commands[0]
+ main_command_opts = dist.get_option_dict(main_command_name)
+ current_command_name = current_command.get_command_name()
+ current_command_opts = dist.get_option_dict(current_command_name)
+ mixin_options_set = self.get_mixin_options_set()
+ for key, value in main_command_opts.items():
+ if key not in current_command_opts and key in mixin_options_set:
+ current_command_opts[key] = value
+
+ @staticmethod
+ @memoize
+ def get_mixin_options_set():
+ keys = set()
+ for (name, _, _) in DistUtilsCommandMixin.mixin_user_options:
+ keys.add(name.rstrip("=").replace("-", "_"))
+ return keys
+
def mixin_finalize_options(self):
- # Bail out on 2nd call to mixin_finalize_options() since that is the
- # build command following the install command when invoking
- # setup.py install
- if not DistUtilsCommandMixin._finalized:
- DistUtilsCommandMixin._finalized = True
+ # The very first we finalize options, record that.
+ if not DistUtilsCommandMixin._static_class_finalized_once:
+ DistUtilsCommandMixin._static_class_finalized_once = True
+
+ # Ensure we finalize once per command object, rather than per
+ # setup.py invocation. We want to have the option values
+ # available in all commands that derive from the mixin.
+ if not self._per_command_mixin_options_finalized:
+ self._per_command_mixin_options_finalized = True
self._do_finalize()
def _do_finalize(self):
+ # is_cross_compile must be set before checking for qtpaths/qmake
+ # because we DON'T want those to be found when cross compiling.
+ # Currently when cross compiling, qt-target-path MUST be used.
+ using_cmake_toolchain_file = False
+ cmake_toolchain_file = None
+ if OPTION["CMAKE_TOOLCHAIN_FILE"]:
+ self.is_cross_compile = True
+ using_cmake_toolchain_file = True
+ cmake_toolchain_file = OPTION["CMAKE_TOOLCHAIN_FILE"]
+ self.cmake_toolchain_file = cmake_toolchain_file
+
if not self._determine_defaults_and_check():
sys.exit(-1)
OPTION['AVOID_PROTECTED_HACK'] = self.avoid_protected_hack
@@ -320,12 +397,62 @@ class DistUtilsCommandMixin(object):
OPTION['QMAKE'] = qmake_abs_path
OPTION['HAS_QMAKE_OPTION'] = self.has_qmake_option
OPTION['QT_VERSION'] = self.qt
+ self.qt_host_path = OPTION['QT_HOST_PATH']
+ OPTION['QT_TARGET_PATH'] = self.qt_target_path
+
+ qt_target_path = None
+ if self.qt_target_path:
+ qt_target_path = self.qt_target_path
+
+ # We use the CMake project to find host Qt if neither qmake or
+ # qtpaths is available. This happens when building the host
+ # tools in the overall cross-building process.
+ use_cmake = False
+ if using_cmake_toolchain_file or \
+ (not self.qmake and not self.qtpaths and self.qt_target_path):
+ use_cmake = True
+
QtInfo().setup(qtpaths_abs_path, self.cmake, qmake_abs_path,
- self.has_qmake_option)
+ self.has_qmake_option,
+ use_cmake=use_cmake,
+ qt_target_path=qt_target_path,
+ cmake_toolchain_file=cmake_toolchain_file)
+
+ try:
+ QtInfo().prefix_dir
+ except Exception as e:
+ if not self.qt_target_path:
+ log.error(
+ "\nCould not find Qt. You can pass the --qt-target-path=<qt-dir> option as a "
+ "hint where to find Qt. Error was:\n\n\n")
+ else:
+ log.error(
+ f"\nCould not find Qt via provided option --qt-target-path={qt_target_path} "
+ "Error was:\n\n\n")
+ raise e
OPTION['CMAKE'] = os.path.abspath(self.cmake)
OPTION['OPENSSL'] = self.openssl
OPTION['SHIBOKEN_CONFIG_DIR'] = self.shiboken_config_dir
+ if self.shiboken_config_dir:
+ _warn_deprecated_option('shiboken-config-dir', 'shiboken-target-path')
+
+ self.shiboken_host_path = OPTION['SHIBOKEN_HOST_PATH']
+ self.shiboken_host_path_query_file = OPTION['SHIBOKEN_HOST_PATH_QUERY_FILE']
+
+ if not self.shiboken_host_path and self.shiboken_host_path_query_file:
+ try:
+ queried_shiboken_host_path = Path(self.shiboken_host_path_query_file).read_text()
+ self.shiboken_host_path = queried_shiboken_host_path
+ OPTION['SHIBOKEN_HOST_PATH'] = queried_shiboken_host_path
+ except Exception as e:
+ log.error(
+ f"\n Could not find shiboken host tools via the query file: "
+ f"{self.shiboken_host_path_query_file:} Error was:\n\n\n")
+ raise e
+
+ OPTION['SHIBOKEN_TARGET_PATH'] = self.shiboken_target_path
+ OPTION['PYTHON_TARGET_PATH'] = self.python_target_path
OPTION['MAKESPEC'] = self.make_spec
OPTION['MACOS_ARCH'] = self.macos_arch
OPTION['MACOS_SYSROOT'] = self.macos_sysroot
@@ -338,6 +465,15 @@ class DistUtilsCommandMixin(object):
OPTION['NO_QT_TOOLS'] = self.no_qt_tools
OPTION['PYSIDE_NUMPY_SUPPORT'] = self.pyside_numpy_support
+ if not self._extra_checks():
+ sys.exit(-1)
+
+ def _extra_checks(self):
+ if self.is_cross_compile and not self.plat_name:
+ log.error(f"No value provided to --plat-name while cross-compiling.")
+ return False
+ return True
+
def _find_qtpaths_in_path(self):
if not self.qtpaths:
self.qtpaths = which("qtpaths")
@@ -354,30 +490,43 @@ class DistUtilsCommandMixin(object):
log.error(f"'{self.cmake}' does not exist.")
return False
- # Enforce usage of qmake in QtInfo if it was given explicitly.
- if self.qmake:
- self.has_qmake_option = True
- _warn_deprecated_option('qmake', 'qtpaths')
-
- # If no option was given explicitly, prefer to find qtpaths
- # in PATH.
- if not self.qmake and not self.qtpaths:
- self._find_qtpaths_in_path()
-
- # If no tool was specified and qtpaths was not found in PATH,
- # ask to provide a path to qtpaths.
- if not self.qtpaths and not self.qmake:
- log.error("No value provided to --qtpaths option. Please provide one to find Qt.")
- return False
-
- # Validate that the given tool path exists.
- if self.qtpaths and not os.path.exists(self.qtpaths):
- log.error(f"The specified qtpaths path '{self.qtpaths}' does not exist.")
- return False
-
- if self.qmake and not os.path.exists(self.qmake):
- log.error(f"The specified qmake path '{self.qmake}' does not exist.")
- return False
+ # When cross-compiling, we only accept the qt-target-path
+ # option and don't rely on auto-searching in PATH or the other
+ # qtpaths / qmake options.
+ # We also don't do auto-searching if qt-target-path is passed
+ # explicitly. This is to help with the building of host tools
+ # while cross-compiling.
+ if not self.is_cross_compile and not self.qt_target_path:
+ # Enforce usage of qmake in QtInfo if it was given explicitly.
+ if self.qmake:
+ self.has_qmake_option = True
+ _warn_deprecated_option('qmake', 'qtpaths')
+
+ # If no option was given explicitly, prefer to find qtpaths
+ # in PATH.
+ if not self.qmake and not self.qtpaths:
+ self._find_qtpaths_in_path()
+
+ # If no tool was specified and qtpaths was not found in PATH,
+ # ask to provide a path to qtpaths.
+ if not self.qtpaths and not self.qmake and not self.qt_target_path:
+ log.error("No value provided to --qtpaths option. Please provide one to find Qt.")
+ return False
+
+ # Validate that the given tool path exists.
+ if self.qtpaths and not os.path.exists(self.qtpaths):
+ log.error(f"The specified qtpaths path '{self.qtpaths}' does not exist.")
+ return False
+
+ if self.qmake and not os.path.exists(self.qmake):
+ log.error(f"The specified qmake path '{self.qmake}' does not exist.")
+ return False
+ else:
+ # Check for existence, but don't require if it's not set. A
+ # check later will be done to see if it's needed.
+ if self.qt_target_path and not os.path.exists(self.qt_target_path):
+ log.error(f"Provided --qt-target-path='{self.qt_target_path}' path does not exist.")
+ return False
if not self.make_spec:
self.make_spec = _AVAILABLE_MKSPECS[0]