Skip to content

Commit b513d1e

Browse files
committed
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]>
1 parent 45548b1 commit b513d1e

File tree

3 files changed

+105
-96
lines changed

3 files changed

+105
-96
lines changed

build_scripts/options.py

Lines changed: 78 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from .log import log, LogLevel
1212
from .qtinfo import QtInfo
13-
from .utils import memoize, which
13+
from .utils import memoize, which, Singleton
1414

1515
_AVAILABLE_MKSPECS = ["ninja", "msvc", "mingw"] if sys.platform == "win32" else ["ninja", "make"]
1616

@@ -41,7 +41,7 @@ def _warn_deprecated_option(option, replacement=None):
4141
log.warning(w)
4242

4343

44-
class Options(object):
44+
class Options(object, metaclass=Singleton):
4545
def __init__(self):
4646

4747
# Dictionary containing values of all the possible options.
@@ -103,86 +103,82 @@ def option_value(self, name, short_option_name=None, remove=True):
103103
self.dict[name] = value
104104
return value
105105

106+
def find_qtpaths(self):
107+
# Skip the first run that will trigger the three different build
108+
# stated of the setup process
109+
if self.dict["internal-build-type"] is None:
110+
return None
111+
# for these command --qtpaths should not be required
112+
no_qtpaths_commands = ["--help", "--help-commands", "--qt-target-path", "build_base_docs"]
106113

107-
options = Options()
108-
109-
110-
def has_option(*args, **kwargs):
111-
return options.has_option(*args, **kwargs)
112-
113-
114-
def option_value(*args, **kwargs):
115-
return options.option_value(*args, **kwargs)
116-
114+
for no_qtpaths_command in no_qtpaths_commands:
115+
if any(no_qtpaths_command in argument for argument in sys.argv):
116+
return None
117117

118-
def _jobs_option_value():
119-
"""Option value for parallel builds."""
120-
value = option_value('parallel', short_option_name='j')
121-
if value:
122-
return f"-j{value}" if not value.startswith('-j') else value
123-
return ''
118+
qtpaths = self.option_value("qtpaths")
119+
if qtpaths is not None:
120+
return qtpaths
124121

122+
# if qtpaths is not given as cli option, try to find it in PATH
123+
qtpaths = which("qtpaths6")
124+
if qtpaths is not None:
125+
return str(Path(qtpaths).resolve())
125126

126-
def find_qtpaths():
127-
# for these command --qtpaths should not be required
128-
no_qtpaths_commands = ["--help", "--help-commands", "--qt-target-path", "build_base_docs"]
127+
qtpaths = which("qtpaths")
128+
if qtpaths is not None:
129+
return str(Path(qtpaths).resolve())
129130

130-
for no_qtpaths_command in no_qtpaths_commands:
131-
if any(no_qtpaths_command in argument for argument in sys.argv):
132-
return None
131+
if qtpaths is None:
132+
sys.exit(-1)
133133

134-
qtpaths = option_value("qtpaths")
135-
if qtpaths:
136134
return qtpaths
137135

138-
# if qtpaths is not given as cli option, try to find it in PATH
139-
qtpaths = which("qtpaths6")
140-
if qtpaths:
141-
return str(qtpaths.resolve())
142-
143-
qtpaths = which("qtpaths")
144-
if qtpaths:
145-
return str(qtpaths.resolve())
146-
147-
return qtpaths
148-
149-
150-
# Declare options which need to be known when instantiating the setuptools
151-
# commands or even earlier during SetupRunner.run().
152-
OPTION = {
153-
"BUILD_TYPE": option_value("build-type"),
154-
"INTERNAL_BUILD_TYPE": option_value("internal-build-type"),
155-
# number of parallel build jobs
156-
"JOBS": _jobs_option_value(),
157-
# Legacy, not used any more.
158-
"JOM": has_option('jom'),
159-
"MACOS_USE_LIBCPP": has_option("macos-use-libc++"),
160-
"LOG_LEVEL": option_value("log-level", remove=False),
161-
"QUIET": has_option('quiet'),
162-
"VERBOSE_BUILD": has_option('verbose-build'),
163-
"SNAPSHOT_BUILD": has_option("snapshot-build"),
164-
"LIMITED_API": option_value("limited-api"),
165-
"UNOPTIMIZE": option_value("unoptimize"),
166-
"DISABLE_PYI": has_option("disable-pyi"),
167-
"SKIP_MYPY_TEST": has_option("skip-mypy-test"),
168-
"PACKAGE_TIMESTAMP": option_value("package-timestamp"),
169-
# This is used automatically by setuptools.command.install object, to
170-
# specify the final installation location.
171-
"FINAL_INSTALL_PREFIX": option_value("prefix", remove=False),
172-
"CMAKE_TOOLCHAIN_FILE": option_value("cmake-toolchain-file"),
173-
"SHIBOKEN_HOST_PATH": option_value("shiboken-host-path"),
174-
"SHIBOKEN_HOST_PATH_QUERY_FILE": option_value("internal-shiboken-host-path-query-file"),
175-
"QT_HOST_PATH": option_value("qt-host-path"),
176-
# This is used to identify the template for doc builds
177-
"QTPATHS": find_qtpaths()
178-
# This is an optional command line option. If --qtpaths is not provided via command-line,
179-
# then qtpaths is checked inside PATH variable
180-
}
181-
182-
_deprecated_option_jobs = option_value('jobs')
183-
if _deprecated_option_jobs:
184-
_warn_deprecated_option('jobs', 'parallel')
185-
OPTION["JOBS"] = _deprecated_option_jobs
136+
def _jobs_option_value(self):
137+
"""Option value for parallel builds."""
138+
value = self.option_value('parallel', short_option_name='j')
139+
140+
_deprecated_option_jobs = self.option_value('jobs')
141+
if _deprecated_option_jobs:
142+
_warn_deprecated_option('jobs', 'parallel')
143+
value = _deprecated_option_jobs
144+
145+
if value:
146+
return f"-j{value}" if not value.startswith('-j') else value
147+
return ''
148+
149+
def resolve(self):
150+
return {
151+
"BUILD_TYPE": self.option_value("build-type"),
152+
"INTERNAL_BUILD_TYPE": self.option_value("internal-build-type"),
153+
# number of parallel build jobs
154+
"JOBS": self._jobs_option_value(),
155+
# Legacy, not used any more.
156+
"JOM": self.has_option('jom'),
157+
"MACOS_USE_LIBCPP": self.has_option("macos-use-libc++"),
158+
"LOG_LEVEL": self.option_value("log-level", remove=False),
159+
"QUIET": self.has_option('quiet'),
160+
"VERBOSE_BUILD": self.has_option('verbose-build'),
161+
"SNAPSHOT_BUILD": self.has_option("snapshot-build"),
162+
"LIMITED_API": self.option_value("limited-api"),
163+
"UNOPTIMIZE": self.option_value("unoptimize"),
164+
"DISABLE_PYI": self.has_option("disable-pyi"),
165+
"SKIP_MYPY_TEST": self.has_option("skip-mypy-test"),
166+
"PACKAGE_TIMESTAMP": self.option_value("package-timestamp"),
167+
# This is used automatically by setuptools.command.install object, to
168+
# specify the final installation location.
169+
"FINAL_INSTALL_PREFIX": self.option_value("prefix", remove=False),
170+
"CMAKE_TOOLCHAIN_FILE": self.option_value("cmake-toolchain-file"),
171+
"SHIBOKEN_HOST_PATH": self.option_value("shiboken-host-path"),
172+
"SHIBOKEN_HOST_PATH_QUERY_FILE": self.option_value(
173+
"internal-shiboken-host-path-query-file"
174+
),
175+
"QT_HOST_PATH": self.option_value("qt-host-path"),
176+
# This is used to identify the template for doc builds
177+
"QTPATHS": self.find_qtpaths()
178+
# This is an optional command line option.
179+
# If --qtpaths is not provided via command-line,
180+
# then qtpaths is checked inside PATH variable
181+
}
186182

187183

188184
class CommandMixin(object):
@@ -499,12 +495,11 @@ def _do_finalize(self):
499495
except Exception as e:
500496
if not self.qt_target_path:
501497
log.error(
502-
"\nCould not find Qt. You can pass the --qt-target-path=<qt-dir> option "
503-
"as a hint where to find Qt. Error was:\n\n\n")
498+
"Could not find Qt. You can pass the --qt-target-path=<qt-dir> option "
499+
"as a hint where to find Qt.\n")
504500
else:
505-
log.error(
506-
f"\nCould not find Qt via provided option --qt-target-path={qt_target_path}"
507-
"Error was:\n\n\n")
501+
log.error("Could not find Qt via provided option "
502+
f"--qt-target-path={qt_target_path}\n")
508503
raise e
509504

510505
OPTION['CMAKE'] = self.cmake.resolve()
@@ -629,3 +624,7 @@ def _determine_defaults_and_check(self):
629624
return False
630625

631626
return True
627+
628+
629+
# OPTION dictionary that will be imported in other build_scripts
630+
OPTION = Options().resolve()

build_scripts/utils.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@
2929
WindowsError = None
3030

3131

32+
class Singleton(type):
33+
_instances = {}
34+
35+
def __call__(cls, *args, **kwargs):
36+
if cls not in cls._instances:
37+
cls._instances[cls] = super().__call__(*args, **kwargs)
38+
return cls._instances[cls]
39+
40+
3241
def which(name):
3342
"""
3443
Like shutil.which, but accepts a string or a PathLike and returns a Path
@@ -38,9 +47,8 @@ def which(name):
3847
if isinstance(name, Path):
3948
name = str(name)
4049
path = shutil.which(name)
41-
if path is None:
42-
raise TypeError("None was returned")
43-
path = Path(path)
50+
if path is not None:
51+
path = Path(path)
4452
except TypeError as e:
4553
log.error(f"{name} was not found in PATH: {e}")
4654
return path

coin/instructions_utils.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,35 @@
99
import sys
1010
from pathlib import Path
1111

12-
from build_scripts.options import has_option, option_value
12+
from build_scripts.options import Options
1313
from build_scripts.utils import (parse_cmake_conf_assignments_by_key,
1414
remove_tree, run_instruction)
1515

16+
options = Options()
17+
1618

1719
class CI:
1820
def __init__(self):
1921
# Values must match COIN thrift
20-
self.HOST_OS = option_value("os")
21-
self.TARGET_OS = option_value("targetOs")
22-
self.HOST_ARCH = option_value("hostArch")
23-
self.TARGET_ARCH = option_value("targetArch")
24-
self.HOST_OS_VER = option_value("osVer")
25-
self.ENV_INSTALL_DIR = option_value("instdir")
26-
self.ENV_AGENT_DIR = option_value("agentdir") or "."
27-
self.COMPILER = option_value("compiler")
28-
self.USE_SCCACHE = option_value("compiler-launcher")
29-
self.INTEGRATION_ID = option_value("coinIntegrationId") or str(
22+
self.HOST_OS = options.option_value("os")
23+
self.TARGET_OS = options.option_value("targetOs")
24+
self.HOST_ARCH = options.option_value("hostArch")
25+
self.TARGET_ARCH = options.option_value("targetArch")
26+
self.HOST_OS_VER = options.option_value("osVer")
27+
self.ENV_INSTALL_DIR = options.option_value("instdir")
28+
self.ENV_AGENT_DIR = options.option_value("agentdir") or "."
29+
self.COMPILER = options.option_value("compiler")
30+
self.USE_SCCACHE = options.option_value("compiler-launcher")
31+
self.INTEGRATION_ID = options.option_value("coinIntegrationId") or str(
3032
calendar.timegm(datetime.datetime.now().timetuple())
3133
)
3234
self.FEATURES = []
33-
_ci_features = option_value("features")
35+
_ci_features = options.option_value("features")
3436
if _ci_features is not None:
3537
for f in _ci_features.split(", "):
3638
self.FEATURES.append(f)
37-
self.RELEASE_CONF = has_option("packaging")
38-
self.TEST_PHASE = option_value("phase")
39+
self.RELEASE_CONF = options.has_option("packaging")
40+
self.TEST_PHASE = options.option_value("phase")
3941
if self.TEST_PHASE not in ["ALL", "BUILD"]:
4042
self.TEST_PHASE = "ALL"
4143

0 commit comments

Comments
 (0)