diff options
author | Cristián Maureira-Fredes <[email protected]> | 2024-12-12 12:53:05 +0100 |
---|---|---|
committer | Cristián Maureira-Fredes <[email protected]> | 2024-12-18 10:08:01 +0100 |
commit | b513d1e0ba84f997561f624c73ee54ab91581861 (patch) | |
tree | 200a3f6d4e0dbb1795e67a8d2626bb5c11cb0e05 | |
parent | 45548b18a9e73012521a7b16a234e9689da0d9ab (diff) |
build: options as a singleton
Avoid finding the dynamic options each time the OPTION dictionary
was imported in the different build_scripts files.
Now each setup.py invocation will have the same object.
Pick-to: 6.8
Change-Id: Ic556d572e77e54fe27603332b7d2f99697eab86c
Reviewed-by: Friedemann Kleint <[email protected]>
-rw-r--r-- | build_scripts/options.py | 157 | ||||
-rw-r--r-- | build_scripts/utils.py | 14 | ||||
-rw-r--r-- | coin/instructions_utils.py | 30 |
3 files changed, 105 insertions, 96 deletions
diff --git a/build_scripts/options.py b/build_scripts/options.py index 7a03afbfe..f20e79167 100644 --- a/build_scripts/options.py +++ b/build_scripts/options.py @@ -10,7 +10,7 @@ from pathlib import Path from .log import log, LogLevel from .qtinfo import QtInfo -from .utils import memoize, which +from .utils import memoize, which, Singleton _AVAILABLE_MKSPECS = ["ninja", "msvc", "mingw"] if sys.platform == "win32" else ["ninja", "make"] @@ -41,7 +41,7 @@ def _warn_deprecated_option(option, replacement=None): log.warning(w) -class Options(object): +class Options(object, metaclass=Singleton): def __init__(self): # Dictionary containing values of all the possible options. @@ -103,86 +103,82 @@ class Options(object): self.dict[name] = value return value + def find_qtpaths(self): + # Skip the first run that will trigger the three different build + # stated of the setup process + if self.dict["internal-build-type"] is None: + return None + # for these command --qtpaths should not be required + no_qtpaths_commands = ["--help", "--help-commands", "--qt-target-path", "build_base_docs"] -options = Options() - - -def has_option(*args, **kwargs): - return options.has_option(*args, **kwargs) - - -def option_value(*args, **kwargs): - return options.option_value(*args, **kwargs) - + for no_qtpaths_command in no_qtpaths_commands: + if any(no_qtpaths_command in argument for argument in sys.argv): + return None -def _jobs_option_value(): - """Option value for parallel builds.""" - value = option_value('parallel', short_option_name='j') - if value: - return f"-j{value}" if not value.startswith('-j') else value - return '' + qtpaths = self.option_value("qtpaths") + if qtpaths is not None: + return qtpaths + # if qtpaths is not given as cli option, try to find it in PATH + qtpaths = which("qtpaths6") + if qtpaths is not None: + return str(Path(qtpaths).resolve()) -def find_qtpaths(): - # for these command --qtpaths should not be required - no_qtpaths_commands = ["--help", "--help-commands", "--qt-target-path", "build_base_docs"] + qtpaths = which("qtpaths") + if qtpaths is not None: + return str(Path(qtpaths).resolve()) - for no_qtpaths_command in no_qtpaths_commands: - if any(no_qtpaths_command in argument for argument in sys.argv): - return None + if qtpaths is None: + sys.exit(-1) - qtpaths = option_value("qtpaths") - if qtpaths: return qtpaths - # if qtpaths is not given as cli option, try to find it in PATH - qtpaths = which("qtpaths6") - if qtpaths: - return str(qtpaths.resolve()) - - qtpaths = which("qtpaths") - if qtpaths: - return str(qtpaths.resolve()) - - return qtpaths - - -# Declare options which need to be known when instantiating the setuptools -# commands or even earlier during SetupRunner.run(). -OPTION = { - "BUILD_TYPE": option_value("build-type"), - "INTERNAL_BUILD_TYPE": option_value("internal-build-type"), - # number of parallel build jobs - "JOBS": _jobs_option_value(), - # Legacy, not used any more. - "JOM": has_option('jom'), - "MACOS_USE_LIBCPP": has_option("macos-use-libc++"), - "LOG_LEVEL": option_value("log-level", remove=False), - "QUIET": has_option('quiet'), - "VERBOSE_BUILD": has_option('verbose-build'), - "SNAPSHOT_BUILD": has_option("snapshot-build"), - "LIMITED_API": option_value("limited-api"), - "UNOPTIMIZE": option_value("unoptimize"), - "DISABLE_PYI": has_option("disable-pyi"), - "SKIP_MYPY_TEST": has_option("skip-mypy-test"), - "PACKAGE_TIMESTAMP": option_value("package-timestamp"), - # This is used automatically by setuptools.command.install object, to - # specify the final installation location. - "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 - "QTPATHS": find_qtpaths() - # This is an optional command line option. If --qtpaths is not provided via command-line, - # then qtpaths is checked inside PATH variable -} - -_deprecated_option_jobs = option_value('jobs') -if _deprecated_option_jobs: - _warn_deprecated_option('jobs', 'parallel') - OPTION["JOBS"] = _deprecated_option_jobs + def _jobs_option_value(self): + """Option value for parallel builds.""" + value = self.option_value('parallel', short_option_name='j') + + _deprecated_option_jobs = self.option_value('jobs') + if _deprecated_option_jobs: + _warn_deprecated_option('jobs', 'parallel') + value = _deprecated_option_jobs + + if value: + return f"-j{value}" if not value.startswith('-j') else value + return '' + + def resolve(self): + return { + "BUILD_TYPE": self.option_value("build-type"), + "INTERNAL_BUILD_TYPE": self.option_value("internal-build-type"), + # number of parallel build jobs + "JOBS": self._jobs_option_value(), + # Legacy, not used any more. + "JOM": self.has_option('jom'), + "MACOS_USE_LIBCPP": self.has_option("macos-use-libc++"), + "LOG_LEVEL": self.option_value("log-level", remove=False), + "QUIET": self.has_option('quiet'), + "VERBOSE_BUILD": self.has_option('verbose-build'), + "SNAPSHOT_BUILD": self.has_option("snapshot-build"), + "LIMITED_API": self.option_value("limited-api"), + "UNOPTIMIZE": self.option_value("unoptimize"), + "DISABLE_PYI": self.has_option("disable-pyi"), + "SKIP_MYPY_TEST": self.has_option("skip-mypy-test"), + "PACKAGE_TIMESTAMP": self.option_value("package-timestamp"), + # This is used automatically by setuptools.command.install object, to + # specify the final installation location. + "FINAL_INSTALL_PREFIX": self.option_value("prefix", remove=False), + "CMAKE_TOOLCHAIN_FILE": self.option_value("cmake-toolchain-file"), + "SHIBOKEN_HOST_PATH": self.option_value("shiboken-host-path"), + "SHIBOKEN_HOST_PATH_QUERY_FILE": self.option_value( + "internal-shiboken-host-path-query-file" + ), + "QT_HOST_PATH": self.option_value("qt-host-path"), + # This is used to identify the template for doc builds + "QTPATHS": self.find_qtpaths() + # This is an optional command line option. + # If --qtpaths is not provided via command-line, + # then qtpaths is checked inside PATH variable + } class CommandMixin(object): @@ -499,12 +495,11 @@ class CommandMixin(object): 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") + "Could not find Qt. You can pass the --qt-target-path=<qt-dir> option " + "as a hint where to find Qt.\n") else: - log.error( - f"\nCould not find Qt via provided option --qt-target-path={qt_target_path}" - "Error was:\n\n\n") + log.error("Could not find Qt via provided option " + f"--qt-target-path={qt_target_path}\n") raise e OPTION['CMAKE'] = self.cmake.resolve() @@ -629,3 +624,7 @@ class CommandMixin(object): return False return True + + +# OPTION dictionary that will be imported in other build_scripts +OPTION = Options().resolve() diff --git a/build_scripts/utils.py b/build_scripts/utils.py index 43ff9e003..9d021c81d 100644 --- a/build_scripts/utils.py +++ b/build_scripts/utils.py @@ -29,6 +29,15 @@ except NameError: WindowsError = None +class Singleton(type): + _instances = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super().__call__(*args, **kwargs) + return cls._instances[cls] + + def which(name): """ Like shutil.which, but accepts a string or a PathLike and returns a Path @@ -38,9 +47,8 @@ def which(name): if isinstance(name, Path): name = str(name) path = shutil.which(name) - if path is None: - raise TypeError("None was returned") - path = Path(path) + if path is not None: + path = Path(path) except TypeError as e: log.error(f"{name} was not found in PATH: {e}") return path diff --git a/coin/instructions_utils.py b/coin/instructions_utils.py index f8ea5a593..176a6d225 100644 --- a/coin/instructions_utils.py +++ b/coin/instructions_utils.py @@ -9,33 +9,35 @@ import site import sys from pathlib import Path -from build_scripts.options import has_option, option_value +from build_scripts.options import Options from build_scripts.utils import (parse_cmake_conf_assignments_by_key, remove_tree, run_instruction) +options = Options() + class CI: def __init__(self): # Values must match COIN thrift - self.HOST_OS = option_value("os") - self.TARGET_OS = option_value("targetOs") - self.HOST_ARCH = option_value("hostArch") - self.TARGET_ARCH = option_value("targetArch") - self.HOST_OS_VER = option_value("osVer") - self.ENV_INSTALL_DIR = option_value("instdir") - self.ENV_AGENT_DIR = option_value("agentdir") or "." - self.COMPILER = option_value("compiler") - self.USE_SCCACHE = option_value("compiler-launcher") - self.INTEGRATION_ID = option_value("coinIntegrationId") or str( + self.HOST_OS = options.option_value("os") + self.TARGET_OS = options.option_value("targetOs") + self.HOST_ARCH = options.option_value("hostArch") + self.TARGET_ARCH = options.option_value("targetArch") + self.HOST_OS_VER = options.option_value("osVer") + self.ENV_INSTALL_DIR = options.option_value("instdir") + self.ENV_AGENT_DIR = options.option_value("agentdir") or "." + self.COMPILER = options.option_value("compiler") + self.USE_SCCACHE = options.option_value("compiler-launcher") + self.INTEGRATION_ID = options.option_value("coinIntegrationId") or str( calendar.timegm(datetime.datetime.now().timetuple()) ) self.FEATURES = [] - _ci_features = option_value("features") + _ci_features = options.option_value("features") if _ci_features is not None: for f in _ci_features.split(", "): self.FEATURES.append(f) - self.RELEASE_CONF = has_option("packaging") - self.TEST_PHASE = option_value("phase") + self.RELEASE_CONF = options.has_option("packaging") + self.TEST_PHASE = options.option_value("phase") if self.TEST_PHASE not in ["ALL", "BUILD"]: self.TEST_PHASE = "ALL" |