@@ -116,8 +116,10 @@ def _getuserbase():
116116 if env_base :
117117 return env_base
118118
119- # Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories
120- if sys .platform in {"emscripten" , "ios" , "tvos" , "vxworks" , "wasi" , "watchos" }:
119+ # Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories.
120+ # Use _PYTHON_HOST_PLATFORM to get the correct platform when cross-compiling.
121+ system_name = os .environ .get ('_PYTHON_HOST_PLATFORM' , sys .platform ).split ('-' )[0 ]
122+ if system_name in {"emscripten" , "ios" , "tvos" , "vxworks" , "wasi" , "watchos" }:
121123 return None
122124
123125 def joinuser (* args ):
@@ -323,44 +325,72 @@ def get_default_scheme():
323325
324326def get_makefile_filename ():
325327 """Return the path of the Makefile."""
328+
329+ # GH-127429: When cross-compiling, use the Makefile from the target, instead of the host Python.
330+ if cross_base := os .environ .get ('_PYTHON_PROJECT_BASE' ):
331+ return os .path .join (cross_base , 'Makefile' )
332+
326333 if _PYTHON_BUILD :
327334 return os .path .join (_PROJECT_BASE , "Makefile" )
335+
328336 if hasattr (sys , 'abiflags' ):
329337 config_dir_name = f'config-{ _PY_VERSION_SHORT } { sys .abiflags } '
330338 else :
331339 config_dir_name = 'config'
340+
332341 if hasattr (sys .implementation , '_multiarch' ):
333342 config_dir_name += f'-{ sys .implementation ._multiarch } '
343+
334344 return os .path .join (get_path ('stdlib' ), config_dir_name , 'Makefile' )
335345
336346
347+ def _import_from_directory (path , name ):
348+ if name not in sys .modules :
349+ import importlib .machinery
350+ import importlib .util
351+
352+ spec = importlib .machinery .PathFinder .find_spec (name , [path ])
353+ module = importlib .util .module_from_spec (spec )
354+ spec .loader .exec_module (module )
355+ sys .modules [name ] = module
356+ return sys .modules [name ]
357+
358+
337359def _get_sysconfigdata_name ():
338360 multiarch = getattr (sys .implementation , '_multiarch' , '' )
339361 return os .environ .get (
340362 '_PYTHON_SYSCONFIGDATA_NAME' ,
341363 f'_sysconfigdata_{ sys .abiflags } _{ sys .platform } _{ multiarch } ' ,
342364 )
343365
366+
367+ def _get_sysconfigdata ():
368+ import importlib
369+
370+ name = _get_sysconfigdata_name ()
371+ path = os .environ .get ('_PYTHON_SYSCONFIGDATA_PATH' )
372+ module = _import_from_directory (path , name ) if path else importlib .import_module (name )
373+
374+ return module .build_time_vars
375+
376+
377+ def _installation_is_relocated ():
378+ """Is the Python installation running from a different prefix than what was targetted when building?"""
379+ if os .name != 'posix' :
380+ raise NotImplementedError ('sysconfig._installation_is_relocated() is currently only supported on POSIX' )
381+
382+ data = _get_sysconfigdata ()
383+ return (
384+ data ['prefix' ] != getattr (sys , 'base_prefix' , '' )
385+ or data ['exec_prefix' ] != getattr (sys , 'base_exec_prefix' , '' )
386+ )
387+
388+
344389def _init_posix (vars ):
345390 """Initialize the module as appropriate for POSIX systems."""
346- # _sysconfigdata is generated at build time, see _generate_posix_vars()
347- name = _get_sysconfigdata_name ( )
391+ # GH-126920: Make sure we don't overwrite any of the keys already set
392+ vars . update ( _get_sysconfigdata () | vars )
348393
349- # For cross builds, the path to the target's sysconfigdata must be specified
350- # so it can be imported. It cannot be in PYTHONPATH, as foreign modules in
351- # sys.path can cause crashes when loaded by the host interpreter.
352- # Rely on truthiness as a valueless env variable is still an empty string.
353- # See OS X note in _generate_posix_vars re _sysconfigdata.
354- if (path := os .environ .get ('_PYTHON_SYSCONFIGDATA_PATH' )):
355- from importlib .machinery import FileFinder , SourceFileLoader , SOURCE_SUFFIXES
356- from importlib .util import module_from_spec
357- spec = FileFinder (path , (SourceFileLoader , SOURCE_SUFFIXES )).find_spec (name )
358- _temp = module_from_spec (spec )
359- spec .loader .exec_module (_temp )
360- else :
361- _temp = __import__ (name , globals (), locals (), ['build_time_vars' ], 0 )
362- build_time_vars = _temp .build_time_vars
363- vars .update (build_time_vars )
364394
365395def _init_non_posix (vars ):
366396 """Initialize the module as appropriate for NT"""
@@ -371,9 +401,20 @@ def _init_non_posix(vars):
371401 vars ['BINLIBDEST' ] = get_path ('platstdlib' )
372402 vars ['INCLUDEPY' ] = get_path ('include' )
373403
374- # Add EXT_SUFFIX, SOABI, and Py_GIL_DISABLED
404+ # Add EXT_SUFFIX, SOABI, Py_DEBUG, and Py_GIL_DISABLED
375405 vars .update (_sysconfig .config_vars ())
376406
407+ # NOTE: ABIFLAGS is only an emulated value. It is not present during build
408+ # on Windows. sys.abiflags is absent on Windows and vars['abiflags']
409+ # is already widely used to calculate paths, so it should remain an
410+ # empty string.
411+ vars ['ABIFLAGS' ] = '' .join (
412+ (
413+ 't' if vars ['Py_GIL_DISABLED' ] else '' ,
414+ '_d' if vars ['Py_DEBUG' ] else '' ,
415+ ),
416+ )
417+
377418 vars ['LIBDIR' ] = _safe_realpath (os .path .join (get_config_var ('installed_base' ), 'libs' ))
378419 if hasattr (sys , 'dllhandle' ):
379420 dllhandle = _winapi .GetModuleFileName (sys .dllhandle )
@@ -427,7 +468,7 @@ def get_config_h_filename():
427468 """Return the path of pyconfig.h."""
428469 if _PYTHON_BUILD :
429470 if os .name == "nt" :
430- inc_dir = os .path .dirname ( sys . _base_executable )
471+ inc_dir = os .path .join ( _PROJECT_BASE , 'PC' )
431472 else :
432473 inc_dir = _PROJECT_BASE
433474 else :
@@ -468,29 +509,44 @@ def get_path(name, scheme=get_default_scheme(), vars=None, expand=True):
468509def _init_config_vars ():
469510 global _CONFIG_VARS
470511 _CONFIG_VARS = {}
512+
513+ prefix = os .path .normpath (sys .prefix )
514+ exec_prefix = os .path .normpath (sys .exec_prefix )
515+ base_prefix = _BASE_PREFIX
516+ base_exec_prefix = _BASE_EXEC_PREFIX
517+
518+ try :
519+ abiflags = sys .abiflags
520+ except AttributeError :
521+ abiflags = ''
522+
523+ if os .name == 'posix' :
524+ _init_posix (_CONFIG_VARS )
525+ # If we are cross-compiling, load the prefixes from the Makefile instead.
526+ if '_PYTHON_PROJECT_BASE' in os .environ :
527+ prefix = _CONFIG_VARS ['host_prefix' ]
528+ exec_prefix = _CONFIG_VARS ['host_exec_prefix' ]
529+ base_prefix = _CONFIG_VARS ['host_prefix' ]
530+ base_exec_prefix = _CONFIG_VARS ['host_exec_prefix' ]
531+ abiflags = _CONFIG_VARS ['ABIFLAGS' ]
532+
471533 # Normalized versions of prefix and exec_prefix are handy to have;
472534 # in fact, these are the standard versions used most places in the
473535 # Distutils.
474- _PREFIX = os .path .normpath (sys .prefix )
475- _EXEC_PREFIX = os .path .normpath (sys .exec_prefix )
476- _CONFIG_VARS ['prefix' ] = _PREFIX # FIXME: This gets overwriten by _init_posix.
477- _CONFIG_VARS ['exec_prefix' ] = _EXEC_PREFIX # FIXME: This gets overwriten by _init_posix.
536+ _CONFIG_VARS ['prefix' ] = prefix
537+ _CONFIG_VARS ['exec_prefix' ] = exec_prefix
478538 _CONFIG_VARS ['py_version' ] = _PY_VERSION
479539 _CONFIG_VARS ['py_version_short' ] = _PY_VERSION_SHORT
480540 _CONFIG_VARS ['py_version_nodot' ] = _PY_VERSION_SHORT_NO_DOT
481- _CONFIG_VARS ['installed_base' ] = _BASE_PREFIX
482- _CONFIG_VARS ['base' ] = _PREFIX
483- _CONFIG_VARS ['installed_platbase' ] = _BASE_EXEC_PREFIX
484- _CONFIG_VARS ['platbase' ] = _EXEC_PREFIX
541+ _CONFIG_VARS ['installed_base' ] = base_prefix
542+ _CONFIG_VARS ['base' ] = prefix
543+ _CONFIG_VARS ['installed_platbase' ] = base_exec_prefix
544+ _CONFIG_VARS ['platbase' ] = exec_prefix
485545 _CONFIG_VARS ['projectbase' ] = _PROJECT_BASE
486546 _CONFIG_VARS ['platlibdir' ] = sys .platlibdir
487547 _CONFIG_VARS ['implementation' ] = _get_implementation ()
488548 _CONFIG_VARS ['implementation_lower' ] = _get_implementation ().lower ()
489- try :
490- _CONFIG_VARS ['abiflags' ] = sys .abiflags
491- except AttributeError :
492- # sys.abiflags may not be defined on all platforms.
493- _CONFIG_VARS ['abiflags' ] = ''
549+ _CONFIG_VARS ['abiflags' ] = abiflags
494550 try :
495551 _CONFIG_VARS ['py_version_nodot_plat' ] = sys .winver .replace ('.' , '' )
496552 except AttributeError :
@@ -499,8 +555,6 @@ def _init_config_vars():
499555 if os .name == 'nt' :
500556 _init_non_posix (_CONFIG_VARS )
501557 _CONFIG_VARS ['VPATH' ] = sys ._vpath
502- if os .name == 'posix' :
503- _init_posix (_CONFIG_VARS )
504558 if _HAS_USER_BASE :
505559 # Setting 'userbase' is done below the call to the
506560 # init function to enable using 'get_config_var' in
@@ -550,23 +604,23 @@ def get_config_vars(*args):
550604 global _CONFIG_VARS_INITIALIZED
551605
552606 # Avoid claiming the lock once initialization is complete.
553- if not _CONFIG_VARS_INITIALIZED :
607+ if _CONFIG_VARS_INITIALIZED :
608+ # GH-126789: If sys.prefix or sys.exec_prefix were updated, invalidate the cache.
609+ prefix = os .path .normpath (sys .prefix )
610+ exec_prefix = os .path .normpath (sys .exec_prefix )
611+ if _CONFIG_VARS ['prefix' ] != prefix or _CONFIG_VARS ['exec_prefix' ] != exec_prefix :
612+ with _CONFIG_VARS_LOCK :
613+ _CONFIG_VARS_INITIALIZED = False
614+ _init_config_vars ()
615+ else :
616+ # Initialize the config_vars cache.
554617 with _CONFIG_VARS_LOCK :
555618 # Test again with the lock held to avoid races. Note that
556619 # we test _CONFIG_VARS here, not _CONFIG_VARS_INITIALIZED,
557620 # to ensure that recursive calls to get_config_vars()
558621 # don't re-enter init_config_vars().
559622 if _CONFIG_VARS is None :
560623 _init_config_vars ()
561- else :
562- # If the site module initialization happened after _CONFIG_VARS was
563- # initialized, a virtual environment might have been activated, resulting in
564- # variables like sys.prefix changing their value, so we need to re-init the
565- # config vars (see GH-126789).
566- if _CONFIG_VARS ['base' ] != os .path .normpath (sys .prefix ):
567- with _CONFIG_VARS_LOCK :
568- _CONFIG_VARS_INITIALIZED = False
569- _init_config_vars ()
570624
571625 if args :
572626 vals = []
@@ -627,34 +681,34 @@ def get_platform():
627681
628682 # Set for cross builds explicitly
629683 if "_PYTHON_HOST_PLATFORM" in os .environ :
630- return os .environ ["_PYTHON_HOST_PLATFORM" ]
631-
632- # Try to distinguish various flavours of Unix
633- osname , host , release , version , machine = os . uname ()
634-
635- # Convert the OS name to lowercase, remove '/' characters, and translate
636- # spaces (for "Power Macintosh")
637- osname = osname . lower (). replace ( '/' , '' )
638- machine = machine . replace (' ' , '_ ' )
639- machine = machine .replace ('/ ' , '- ' )
640-
641- if osname [: 5 ] == "linux" :
642- if sys .platform == "android" :
643- osname = "android"
644- release = get_config_var ("ANDROID_API_LEVEL" )
645-
646- # Wheel tags use the ABI names from Android's own tools.
647- machine = {
648- "x86_64" : "x86_64" ,
649- "i686" : "x86" ,
650- "aarch64" : "arm64_v8a" ,
651- "armv7l" : "armeabi_v7a" ,
652- }[machine ]
653- else :
654- # At least on Linux/Intel, 'machine' is the processor --
655- # i386, etc.
656- # XXX what about Alpha, SPARC, etc?
657- return f"{ osname } -{ machine } "
684+ osname , _ , machine = os .environ ["_PYTHON_HOST_PLATFORM" ]. partition ( '-' )
685+ release = None
686+ else :
687+ # Try to distinguish various flavours of Unix
688+ osname , host , release , version , machine = os . uname ()
689+
690+ # Convert the OS name to lowercase, remove '/' characters, and translate
691+ # spaces (for "Power Macintosh" )
692+ osname = osname . lower (). replace ('/ ' , '' )
693+ machine = machine .replace (' ' , '_ ' )
694+ machine = machine . replace ( '/' , '-' )
695+
696+ if osname == "android" or sys .platform == "android" :
697+ osname = "android"
698+ release = get_config_var ("ANDROID_API_LEVEL" )
699+
700+ # Wheel tags use the ABI names from Android's own tools.
701+ machine = {
702+ "x86_64" : "x86_64" ,
703+ "i686" : "x86" ,
704+ "aarch64" : "arm64_v8a" ,
705+ "armv7l" : "armeabi_v7a" ,
706+ }[machine ]
707+ elif osname == "linux" :
708+ # At least on Linux/Intel, 'machine' is the processor --
709+ # i386, etc.
710+ # XXX what about Alpha, SPARC, etc?
711+ return f"{ osname } -{ machine } "
658712 elif osname [:5 ] == "sunos" :
659713 if release [0 ] >= "5" : # SunOS 5 == Solaris 2
660714 osname = "solaris"
@@ -686,7 +740,7 @@ def get_platform():
686740 get_config_vars (),
687741 osname , release , machine )
688742
689- return f" { osname } - { release } - { machine } "
743+ return '-' . join ( map ( str , filter ( None , ( osname , release , machine ))))
690744
691745
692746def get_python_version ():
@@ -705,6 +759,15 @@ def expand_makefile_vars(s, vars):
705759 variable expansions; if 'vars' is the output of 'parse_makefile()',
706760 you're fine. Returns a variable-expanded version of 's'.
707761 """
762+
763+ import warnings
764+ warnings .warn (
765+ 'sysconfig.expand_makefile_vars is deprecated and will be removed in '
766+ 'Python 3.16. Use sysconfig.get_paths(vars=...) instead.' ,
767+ DeprecationWarning ,
768+ stacklevel = 2 ,
769+ )
770+
708771 import re
709772
710773 _findvar1_rx = r"\$\(([A-Za-z][A-Za-z0-9_]*)\)"
0 commit comments