Skip to content

Commit 43fcae5

Browse files
committed
python: validate python.build_config version and expand relative paths
Signed-off-by: Filipe Laíns <[email protected]>
1 parent 6f172df commit 43fcae5

File tree

1 file changed

+89
-8
lines changed

1 file changed

+89
-8
lines changed

mesonbuild/dependencies/python.py

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,94 @@ def __init__(self, name: str, environment: Environment, kwargs: T.Dict[str, T.An
7474
self.compile_args = self.get_config_value(['--cflags'], 'compile_args')
7575

7676

77+
78+
class PythonBuildConfig:
79+
"""PEP 739 build-details.json config file."""
80+
81+
"""Schema version currently implemented."""
82+
IMPLEMENTED_VERSION: T.Final[str] = '1.0'
83+
"""Path keys — may be relative, need to be expanded."""
84+
_PATH_KEYS = (
85+
'base_interpreter',
86+
'libpython.dynamic',
87+
'libpython.dynamic_stableabi',
88+
'libpython.static',
89+
'c_api.headers',
90+
'c_api.pkgconfig_path',
91+
)
92+
93+
def __init__(self, path: str) -> None:
94+
self._path = Path(path)
95+
96+
try:
97+
self._data = json.loads(self._path.read_text(encoding='utf8'))
98+
except OSError as e:
99+
raise DependencyException(f'Failed to read python.build_config: {e}') from e
100+
101+
self._validate_data()
102+
self._expand_paths()
103+
104+
def __getitem__(self, key: str) -> T.Any:
105+
value = self._data
106+
for part in key.split('.'):
107+
value = value[part]
108+
return value
109+
110+
def __contains__(self, key: str) -> bool:
111+
try:
112+
self[key]
113+
except KeyError:
114+
return False
115+
else:
116+
return True
117+
118+
def get(self, key: str, default: T.Any = None) -> T.Any:
119+
try:
120+
self[key]
121+
except KeyError:
122+
return default
123+
124+
def _validate_data(self) -> None:
125+
schema_version = self._data['schema_version']
126+
if mesonlib.version_compare(schema_version, '< 1.0'):
127+
raise DependencyException(f'Invalid schema_version in python.build_config: {schema_version}')
128+
if mesonlib.version_compare(schema_version, '>= 2.0'):
129+
raise DependencyException(
130+
f'Unsupported schema_version {schema_version!r} in python.build_config, '
131+
f'but we only implement suport for {self.IMPLEMENTED_VERSION!r}'
132+
)
133+
# Schema version that we currently understand
134+
if mesonlib.version_compare(schema_version, f'> {self.IMPLEMENTED_VERSION}'):
135+
mlog.info(
136+
f'python.build_config has schema_version {schema_version!r}, '
137+
f'but we only implement suport for {self.IMPLEMENTED_VERSION!r}, '
138+
'new functionality might be missing'
139+
)
140+
141+
def _expand_paths(self) -> None:
142+
"""Expand relative path (they're relative to base_prefix)."""
143+
for key in self._PATH_KEYS:
144+
if key not in self:
145+
continue
146+
parent, _, child = key.rpartition('.')
147+
container = self[parent] if parent else self._data
148+
path = Path(container[child])
149+
if not path.is_absolute():
150+
container[child] = os.fspath(self.base_prefix / path)
151+
152+
@property
153+
def config_path(self) -> Path:
154+
return self._path
155+
156+
@functools.cached_property
157+
def base_prefix(self) -> Path:
158+
path = Path(self._data['base_prefix'])
159+
if path.is_absolute():
160+
return path
161+
# Non-absolute paths are relative to the build config directory
162+
return self.config_path.parent / path
163+
164+
77165
class BasicPythonExternalProgram(ExternalProgram):
78166
def __init__(self, name: str, command: T.Optional[T.List[str]] = None,
79167
ext_prog: T.Optional[ExternalProgram] = None,
@@ -87,14 +175,7 @@ def __init__(self, name: str, command: T.Optional[T.List[str]] = None,
87175
self.cached_version = None
88176
self.version_arg = '--version'
89177

90-
self.build_config = None
91-
92-
if build_config_path:
93-
try:
94-
with open(build_config_path, encoding='utf8') as f:
95-
self.build_config = json.load(f)
96-
except OSError as e:
97-
raise DependencyException(f'Failed to read python.build_config: {e}') from e
178+
self.build_config = PythonBuildConfig(build_config_path) if build_config_path else None
98179

99180
# We want strong key values, so we always populate this with bogus data.
100181
# Otherwise to make the type checkers happy we'd have to do .get() for

0 commit comments

Comments
 (0)