Skip to content

Commit

Permalink
python module: Use sys_root's Python sysconfigdata to get EXT_SUFFIX
Browse files Browse the repository at this point in the history
Until now, the build host's EXT_SUFFIX was used, resulting in native
extensions being build and installed with the wrong filename.

To do this, we load the sys_root sysconfigdata from its stdlib
directory. We assume that the same Python version as the build host
exists within the sys_root with the same stdlib directory, save for the
prefix. If not, we fall back to the build host, as we did before.

If the machine file points python at a Python within sys_root then this
should still work. You should probably only run a sys_root's binary
using some kind of wrapper, but either way, sysconfig will still return
the correct values.

We only read EXT_SUFFIX from the sys_root because paths are expected to
be the same, save for the prefix, and other info such as the platform
and limited API suffix cannot be determined this way.

We could potentially guess the limited API suffix from the platform, as
there are only a small number of possible values, but this can be done
separately.

Closes: mesonbuild#7049
  • Loading branch information
chewi committed Aug 31, 2023
1 parent e3a71a7 commit bbc7289
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 3 deletions.
13 changes: 11 additions & 2 deletions mesonbuild/dependencies/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from .factory import DependencyGenerator
from ..environment import Environment
from ..mesonlib import MachineChoice
from ..modules import ModuleState

class PythonIntrospectionDict(TypedDict):

Expand Down Expand Up @@ -108,13 +109,21 @@ def _check_version(self, version: str) -> bool:
return mesonlib.version_compare(version, '>= 3.0')
return True

def sanity(self) -> bool:
def sanity(self, state: T.Optional['ModuleState'] = None) -> bool:
# Sanity check, we expect to have something that at least quacks in tune

import importlib.resources

# Pass the sys_root with its prefix as the first arg to python_info.py
# if a sys_root is defined. Otherwise pass an empty string.
sys_root_prefix = ''
if state:
sys_root = state.environment.properties[mesonlib.MachineChoice.HOST].get_sys_root()
if sys_root:
sys_root_prefix = sys_root + state.environment.coredata.get_option(mesonlib.OptionKey('prefix'))

with importlib.resources.path('mesonbuild.scripts', 'python_info.py') as f:
cmd = self.get_command() + [str(f)]
cmd = self.get_command() + [str(f), sys_root_prefix]
p, stdout, stderr = mesonlib.Popen_safe(cmd)

try:
Expand Down
2 changes: 1 addition & 1 deletion mesonbuild/modules/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class PythonExternalProgram(BasicPythonExternalProgram):
run_bytecompile: T.ClassVar[T.Dict[str, bool]] = {}

def sanity(self, state: T.Optional['ModuleState'] = None) -> bool:
ret = super().sanity()
ret = super().sanity(state)
if ret:
self.platlib = self._get_path(state, 'platlib')
self.purelib = self._get_path(state, 'purelib')
Expand Down
28 changes: 28 additions & 0 deletions mesonbuild/scripts/python_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,34 @@ def links_against_libpython():
from distutils.sysconfig import get_config_var
suffix = get_config_var('EXT_SUFFIX')
else:
# Determine some information from the sys_root if it is given as the first
# argument. When not given, it is expected to be an empty string.
if len(sys.argv) >= 1 and sys.argv[1]:
import importlib.util, pathlib
try:
# We need to load the sys_root sysconfigdata from its stdlib
# directory. We assume that the same Python version as the build
# host exists within the sys_root with the same stdlib directory,
# save for the prefix. If not, we fall back to the build host.
stdlib = sysconfig.get_path('stdlib', vars={'installed_base': sys.argv[1]})
# We cannot predict exactly what the sys_root sysconfigdata filename
# will be, but there should only be one, so glob to find it.
sysconfigdatas = list(pathlib.Path(stdlib).glob('_sysconfigdata_*.py'))

# Only use the sys_root sysconfigdata if we find exactly one. More
# than one is odd, so it is safer to fall back to the build host.
if len(sysconfigdatas) == 1:
data_spec = importlib.util.spec_from_file_location('_sysconfig_data', sysconfigdatas[0])
data_mod = importlib.util.module_from_spec(data_spec)
data_spec.loader.exec_module(data_mod)
sys_root_variables = data_mod.build_time_vars
# We only read EXT_SUFFIX from the sys_root. Paths are expected
# to be the same, save for the prefix, and other info such as
# the platform cannot be determined this way.
variables['EXT_SUFFIX'] = sys_root_variables.get('EXT_SUFFIX')
except OSError:
pass

suffix = variables.get('EXT_SUFFIX')

limited_api_suffix = None
Expand Down

0 comments on commit bbc7289

Please sign in to comment.