bpo-35854: Fix EnvBuilder and --symlinks in venv on Windows (GH-11700) · python/cpython@a1f9a33 (original) (raw)
`@@ -64,11 +64,10 @@ def create(self, env_dir):
`
64
64
`self.system_site_packages = False
`
65
65
`self.create_configuration(context)
`
66
66
`self.setup_python(context)
`
67
``
`-
if not self.upgrade:
`
68
``
`-
self.setup_scripts(context)
`
69
67
`if self.with_pip:
`
70
68
`self._setup_pip(context)
`
71
69
`if not self.upgrade:
`
``
70
`+
self.setup_scripts(context)
`
72
71
`self.post_setup(context)
`
73
72
`if true_system_site_packages:
`
74
73
`# We had set it to False before, now
`
`@@ -176,6 +175,23 @@ def symlink_or_copy(self, src, dst, relative_symlinks_ok=False):
`
176
175
`logger.warning('Unable to symlink %r to %r', src, dst)
`
177
176
`force_copy = True
`
178
177
`if force_copy:
`
``
178
`+
if os.name == 'nt':
`
``
179
`+
On Windows, we rewrite symlinks to our base python.exe into
`
``
180
`+
copies of venvlauncher.exe
`
``
181
`+
basename, ext = os.path.splitext(os.path.basename(src))
`
``
182
`+
if basename.endswith('_d'):
`
``
183
`+
ext = '_d' + ext
`
``
184
`+
basename = basename[:-2]
`
``
185
`+
if sysconfig.is_python_build(True):
`
``
186
`+
if basename == 'python':
`
``
187
`+
basename = 'venvlauncher'
`
``
188
`+
elif basename == 'pythonw':
`
``
189
`+
basename = 'venvwlauncher'
`
``
190
`+
scripts = os.path.dirname(src)
`
``
191
`+
else:
`
``
192
`+
scripts = os.path.join(os.path.dirname(file), "scripts", "nt")
`
``
193
`+
src = os.path.join(scripts, basename + ext)
`
``
194
+
179
195
`shutil.copyfile(src, dst)
`
180
196
``
181
197
`def setup_python(self, context):
`
`@@ -202,23 +218,31 @@ def setup_python(self, context):
`
202
218
`if not os.path.islink(path):
`
203
219
`os.chmod(path, 0o755)
`
204
220
`else:
`
205
``
`-
For normal cases, the venvlauncher will be copied from
`
206
``
`-
our scripts folder. For builds, we need to copy it
`
207
``
`-
manually.
`
208
``
`-
if sysconfig.is_python_build(True):
`
209
``
`-
suffix = '.exe'
`
210
``
`-
if context.python_exe.lower().endswith('_d.exe'):
`
211
``
`-
suffix = '_d.exe'
`
212
``
-
213
``
`-
src = os.path.join(dirname, "venvlauncher" + suffix)
`
214
``
`-
dst = os.path.join(binpath, context.python_exe)
`
215
``
`-
copier(src, dst)
`
``
221
`+
if self.symlinks:
`
``
222
`+
For symlinking, we need a complete copy of the root directory
`
``
223
`+
If symlinks fail, you'll get unnecessary copies of files, but
`
``
224
`+
we assume that if you've opted into symlinks on Windows then
`
``
225
`+
you know what you're doing.
`
``
226
`+
suffixes = [
`
``
227
`+
f for f in os.listdir(dirname) if
`
``
228
`+
os.path.normcase(os.path.splitext(f)[1]) in ('.exe', '.dll')
`
``
229
`+
]
`
``
230
`+
if sysconfig.is_python_build(True):
`
``
231
`+
suffixes = [
`
``
232
`+
f for f in suffixes if
`
``
233
`+
os.path.normcase(f).startswith(('python', 'vcruntime'))
`
``
234
`+
]
`
``
235
`+
else:
`
``
236
`+
suffixes = ['python.exe', 'python_d.exe', 'pythonw.exe',
`
``
237
`+
'pythonw_d.exe']
`
216
238
``
217
``
`-
src = os.path.join(dirname, "venvwlauncher" + suffix)
`
218
``
`-
dst = os.path.join(binpath, "pythonw" + suffix)
`
219
``
`-
copier(src, dst)
`
``
239
`+
for suffix in suffixes:
`
``
240
`+
src = os.path.join(dirname, suffix)
`
``
241
`+
if os.path.exists(src):
`
``
242
`+
copier(src, os.path.join(binpath, suffix))
`
220
243
``
221
``
`-
copy init.tcl over
`
``
244
`+
if sysconfig.is_python_build(True):
`
``
245
`+
copy init.tcl
`
222
246
`for root, dirs, files in os.walk(context.python_dir):
`
223
247
`if 'init.tcl' in files:
`
224
248
`tcldir = os.path.basename(root)
`
`@@ -304,6 +328,9 @@ def install_scripts(self, context, path):
`
304
328
`dirs.remove(d)
`
305
329
`continue # ignore files in top level
`
306
330
`for f in files:
`
``
331
`+
if (os.name == 'nt' and f.startswith('python')
`
``
332
`+
and f.endswith(('.exe', '.pdb'))):
`
``
333
`+
continue
`
307
334
`srcfile = os.path.join(root, f)
`
308
335
`suffix = root[plen:].split(os.sep)[2:]
`
309
336
`if not suffix:
`