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:

`