mirror of
https://github.com/python/cpython.git
synced 2025-08-04 17:08:35 +00:00
gh-112984 Update Windows build and installer for free-threaded builds (GH-113129)
This commit is contained in:
parent
78fcde039a
commit
f56d132deb
76 changed files with 1437 additions and 245 deletions
|
@ -139,6 +139,11 @@ class EnvBuilder:
|
|||
'check that your PATH environment variable is '
|
||||
'correctly set.')
|
||||
dirname, exename = os.path.split(os.path.abspath(executable))
|
||||
if sys.platform == 'win32':
|
||||
# Always create the simplest name in the venv. It will either be a
|
||||
# link back to executable, or a copy of the appropriate launcher
|
||||
_d = '_d' if os.path.splitext(exename)[0].endswith('_d') else ''
|
||||
exename = f'python{_d}.exe'
|
||||
context.executable = executable
|
||||
context.python_dir = dirname
|
||||
context.python_exe = exename
|
||||
|
@ -222,67 +227,26 @@ class EnvBuilder:
|
|||
args = ' '.join(args)
|
||||
f.write(f'command = {sys.executable} -m venv {args}\n')
|
||||
|
||||
if os.name != 'nt':
|
||||
def symlink_or_copy(self, src, dst, relative_symlinks_ok=False):
|
||||
"""
|
||||
Try symlinking a file, and if that fails, fall back to copying.
|
||||
"""
|
||||
force_copy = not self.symlinks
|
||||
if not force_copy:
|
||||
try:
|
||||
if not os.path.islink(dst): # can't link to itself!
|
||||
if relative_symlinks_ok:
|
||||
assert os.path.dirname(src) == os.path.dirname(dst)
|
||||
os.symlink(os.path.basename(src), dst)
|
||||
else:
|
||||
os.symlink(src, dst)
|
||||
except Exception: # may need to use a more specific exception
|
||||
logger.warning('Unable to symlink %r to %r', src, dst)
|
||||
force_copy = True
|
||||
if force_copy:
|
||||
shutil.copyfile(src, dst)
|
||||
else:
|
||||
def symlink_or_copy(self, src, dst, relative_symlinks_ok=False):
|
||||
"""
|
||||
Try symlinking a file, and if that fails, fall back to copying.
|
||||
"""
|
||||
bad_src = os.path.lexists(src) and not os.path.exists(src)
|
||||
if self.symlinks and not bad_src and not os.path.islink(dst):
|
||||
try:
|
||||
def symlink_or_copy(self, src, dst, relative_symlinks_ok=False):
|
||||
"""
|
||||
Try symlinking a file, and if that fails, fall back to copying.
|
||||
(Unused on Windows, because we can't just copy a failed symlink file: we
|
||||
switch to a different set of files instead.)
|
||||
"""
|
||||
assert os.name != 'nt'
|
||||
force_copy = not self.symlinks
|
||||
if not force_copy:
|
||||
try:
|
||||
if not os.path.islink(dst): # can't link to itself!
|
||||
if relative_symlinks_ok:
|
||||
assert os.path.dirname(src) == os.path.dirname(dst)
|
||||
os.symlink(os.path.basename(src), dst)
|
||||
else:
|
||||
os.symlink(src, dst)
|
||||
return
|
||||
except Exception: # may need to use a more specific exception
|
||||
logger.warning('Unable to symlink %r to %r', src, dst)
|
||||
|
||||
# On Windows, we rewrite symlinks to our base python.exe into
|
||||
# copies of venvlauncher.exe
|
||||
basename, ext = os.path.splitext(os.path.basename(src))
|
||||
srcfn = os.path.join(os.path.dirname(__file__),
|
||||
"scripts",
|
||||
"nt",
|
||||
basename + ext)
|
||||
# Builds or venv's from builds need to remap source file
|
||||
# locations, as we do not put them into Lib/venv/scripts
|
||||
if sysconfig.is_python_build() or not os.path.isfile(srcfn):
|
||||
if basename.endswith('_d'):
|
||||
ext = '_d' + ext
|
||||
basename = basename[:-2]
|
||||
if basename == 'python':
|
||||
basename = 'venvlauncher'
|
||||
elif basename == 'pythonw':
|
||||
basename = 'venvwlauncher'
|
||||
src = os.path.join(os.path.dirname(src), basename + ext)
|
||||
else:
|
||||
src = srcfn
|
||||
if not os.path.exists(src):
|
||||
if not bad_src:
|
||||
logger.warning('Unable to copy %r', src)
|
||||
return
|
||||
|
||||
except Exception: # may need to use a more specific exception
|
||||
logger.warning('Unable to symlink %r to %r', src, dst)
|
||||
force_copy = True
|
||||
if force_copy:
|
||||
shutil.copyfile(src, dst)
|
||||
|
||||
def create_git_ignore_file(self, context):
|
||||
|
@ -298,22 +262,23 @@ class EnvBuilder:
|
|||
'see https://docs.python.org/3/library/venv.html\n')
|
||||
file.write('*\n')
|
||||
|
||||
def setup_python(self, context):
|
||||
"""
|
||||
Set up a Python executable in the environment.
|
||||
if os.name != 'nt':
|
||||
def setup_python(self, context):
|
||||
"""
|
||||
Set up a Python executable in the environment.
|
||||
|
||||
:param context: The information for the environment creation request
|
||||
being processed.
|
||||
"""
|
||||
binpath = context.bin_path
|
||||
path = context.env_exe
|
||||
copier = self.symlink_or_copy
|
||||
dirname = context.python_dir
|
||||
if os.name != 'nt':
|
||||
:param context: The information for the environment creation request
|
||||
being processed.
|
||||
"""
|
||||
binpath = context.bin_path
|
||||
path = context.env_exe
|
||||
copier = self.symlink_or_copy
|
||||
dirname = context.python_dir
|
||||
copier(context.executable, path)
|
||||
if not os.path.islink(path):
|
||||
os.chmod(path, 0o755)
|
||||
for suffix in ('python', 'python3', f'python3.{sys.version_info[1]}'):
|
||||
for suffix in ('python', 'python3',
|
||||
f'python3.{sys.version_info[1]}'):
|
||||
path = os.path.join(binpath, suffix)
|
||||
if not os.path.exists(path):
|
||||
# Issue 18807: make copies if
|
||||
|
@ -321,30 +286,105 @@ class EnvBuilder:
|
|||
copier(context.env_exe, path, relative_symlinks_ok=True)
|
||||
if not os.path.islink(path):
|
||||
os.chmod(path, 0o755)
|
||||
else:
|
||||
if self.symlinks:
|
||||
# For symlinking, we need a complete copy of the root directory
|
||||
# If symlinks fail, you'll get unnecessary copies of files, but
|
||||
# we assume that if you've opted into symlinks on Windows then
|
||||
# you know what you're doing.
|
||||
suffixes = [
|
||||
f for f in os.listdir(dirname) if
|
||||
os.path.normcase(os.path.splitext(f)[1]) in ('.exe', '.dll')
|
||||
]
|
||||
if sysconfig.is_python_build():
|
||||
suffixes = [
|
||||
f for f in suffixes if
|
||||
os.path.normcase(f).startswith(('python', 'vcruntime'))
|
||||
]
|
||||
else:
|
||||
suffixes = {'python.exe', 'python_d.exe', 'pythonw.exe', 'pythonw_d.exe'}
|
||||
base_exe = os.path.basename(context.env_exe)
|
||||
suffixes.add(base_exe)
|
||||
|
||||
for suffix in suffixes:
|
||||
src = os.path.join(dirname, suffix)
|
||||
if os.path.lexists(src):
|
||||
copier(src, os.path.join(binpath, suffix))
|
||||
else:
|
||||
def setup_python(self, context):
|
||||
"""
|
||||
Set up a Python executable in the environment.
|
||||
|
||||
:param context: The information for the environment creation request
|
||||
being processed.
|
||||
"""
|
||||
binpath = context.bin_path
|
||||
dirname = context.python_dir
|
||||
exename = os.path.basename(context.env_exe)
|
||||
exe_stem = os.path.splitext(exename)[0]
|
||||
exe_d = '_d' if os.path.normcase(exe_stem).endswith('_d') else ''
|
||||
if sysconfig.is_python_build():
|
||||
scripts = dirname
|
||||
else:
|
||||
scripts = os.path.join(os.path.dirname(__file__),
|
||||
'scripts', 'nt')
|
||||
if not sysconfig.get_config_var("Py_GIL_DISABLED"):
|
||||
python_exe = os.path.join(dirname, f'python{exe_d}.exe')
|
||||
pythonw_exe = os.path.join(dirname, f'pythonw{exe_d}.exe')
|
||||
link_sources = {
|
||||
'python.exe': python_exe,
|
||||
f'python{exe_d}.exe': python_exe,
|
||||
'pythonw.exe': pythonw_exe,
|
||||
f'pythonw{exe_d}.exe': pythonw_exe,
|
||||
}
|
||||
python_exe = os.path.join(scripts, f'venvlauncher{exe_d}.exe')
|
||||
pythonw_exe = os.path.join(scripts, f'venvwlauncher{exe_d}.exe')
|
||||
copy_sources = {
|
||||
'python.exe': python_exe,
|
||||
f'python{exe_d}.exe': python_exe,
|
||||
'pythonw.exe': pythonw_exe,
|
||||
f'pythonw{exe_d}.exe': pythonw_exe,
|
||||
}
|
||||
else:
|
||||
exe_t = f'3.{sys.version_info[1]}t'
|
||||
python_exe = os.path.join(dirname, f'python{exe_t}{exe_d}.exe')
|
||||
pythonw_exe = os.path.join(dirname, f'pythonw{exe_t}{exe_d}.exe')
|
||||
link_sources = {
|
||||
'python.exe': python_exe,
|
||||
f'python{exe_d}.exe': python_exe,
|
||||
f'python{exe_t}.exe': python_exe,
|
||||
f'python{exe_t}{exe_d}.exe': python_exe,
|
||||
'pythonw.exe': pythonw_exe,
|
||||
f'pythonw{exe_d}.exe': pythonw_exe,
|
||||
f'pythonw{exe_t}.exe': pythonw_exe,
|
||||
f'pythonw{exe_t}{exe_d}.exe': pythonw_exe,
|
||||
}
|
||||
python_exe = os.path.join(scripts, f'venvlaunchert{exe_d}.exe')
|
||||
pythonw_exe = os.path.join(scripts, f'venvwlaunchert{exe_d}.exe')
|
||||
copy_sources = {
|
||||
'python.exe': python_exe,
|
||||
f'python{exe_d}.exe': python_exe,
|
||||
f'python{exe_t}.exe': python_exe,
|
||||
f'python{exe_t}{exe_d}.exe': python_exe,
|
||||
'pythonw.exe': pythonw_exe,
|
||||
f'pythonw{exe_d}.exe': pythonw_exe,
|
||||
f'pythonw{exe_t}.exe': pythonw_exe,
|
||||
f'pythonw{exe_t}{exe_d}.exe': pythonw_exe,
|
||||
}
|
||||
|
||||
do_copies = True
|
||||
if self.symlinks:
|
||||
do_copies = False
|
||||
# For symlinking, we need all the DLLs to be available alongside
|
||||
# the executables.
|
||||
link_sources.update({
|
||||
f: os.path.join(dirname, f) for f in os.listdir(dirname)
|
||||
if os.path.normcase(f).startswith(('python', 'vcruntime'))
|
||||
and os.path.normcase(os.path.splitext(f)[1]) == '.dll'
|
||||
})
|
||||
|
||||
to_unlink = []
|
||||
for dest, src in link_sources.items():
|
||||
dest = os.path.join(binpath, dest)
|
||||
try:
|
||||
os.symlink(src, dest)
|
||||
to_unlink.append(dest)
|
||||
except OSError:
|
||||
logger.warning('Unable to symlink %r to %r', src, dst)
|
||||
do_copies = True
|
||||
for f in to_unlink:
|
||||
try:
|
||||
os.unlink(f)
|
||||
except OSError:
|
||||
logger.warning('Failed to clean up symlink %r',
|
||||
f)
|
||||
logger.warning('Retrying with copies')
|
||||
break
|
||||
|
||||
if do_copies:
|
||||
for dest, src in copy_sources.items():
|
||||
dest = os.path.join(binpath, dest)
|
||||
try:
|
||||
shutil.copy2(src, dest)
|
||||
except OSError:
|
||||
logger.warning('Unable to copy %r to %r', src, dest)
|
||||
|
||||
if sysconfig.is_python_build():
|
||||
# copy init.tcl
|
||||
|
@ -437,6 +477,14 @@ class EnvBuilder:
|
|||
"""
|
||||
binpath = context.bin_path
|
||||
plen = len(path)
|
||||
if os.name == 'nt':
|
||||
def skip_file(f):
|
||||
f = os.path.normcase(f)
|
||||
return (f.startswith(('python', 'venv'))
|
||||
and f.endswith(('.exe', '.pdb')))
|
||||
else:
|
||||
def skip_file(f):
|
||||
return False
|
||||
for root, dirs, files in os.walk(path):
|
||||
if root == path: # at top-level, remove irrelevant dirs
|
||||
for d in dirs[:]:
|
||||
|
@ -444,8 +492,7 @@ class EnvBuilder:
|
|||
dirs.remove(d)
|
||||
continue # ignore files in top level
|
||||
for f in files:
|
||||
if (os.name == 'nt' and f.startswith('python')
|
||||
and f.endswith(('.exe', '.pdb'))):
|
||||
if skip_file(f):
|
||||
continue
|
||||
srcfile = os.path.join(root, f)
|
||||
suffix = root[plen:].split(os.sep)[2:]
|
||||
|
@ -456,20 +503,25 @@ class EnvBuilder:
|
|||
if not os.path.exists(dstdir):
|
||||
os.makedirs(dstdir)
|
||||
dstfile = os.path.join(dstdir, f)
|
||||
if os.name == 'nt' and srcfile.endswith(('.exe', '.pdb')):
|
||||
shutil.copy2(srcfile, dstfile)
|
||||
continue
|
||||
with open(srcfile, 'rb') as f:
|
||||
data = f.read()
|
||||
if not srcfile.endswith(('.exe', '.pdb')):
|
||||
try:
|
||||
data = data.decode('utf-8')
|
||||
data = self.replace_variables(data, context)
|
||||
data = data.encode('utf-8')
|
||||
except UnicodeError as e:
|
||||
data = None
|
||||
logger.warning('unable to copy script %r, '
|
||||
'may be binary: %s', srcfile, e)
|
||||
if data is not None:
|
||||
try:
|
||||
new_data = (
|
||||
self.replace_variables(data.decode('utf-8'), context)
|
||||
.encode('utf-8')
|
||||
)
|
||||
except UnicodeError as e:
|
||||
logger.warning('unable to copy script %r, '
|
||||
'may be binary: %s', srcfile, e)
|
||||
continue
|
||||
if new_data == data:
|
||||
shutil.copy2(srcfile, dstfile)
|
||||
else:
|
||||
with open(dstfile, 'wb') as f:
|
||||
f.write(data)
|
||||
f.write(new_data)
|
||||
shutil.copymode(srcfile, dstfile)
|
||||
|
||||
def upgrade_dependencies(self, context):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue