bpo-34977: Use venv redirector instead of original python.exe on Windows (GH-11029)

This commit is contained in:
Steve Dower 2018-12-10 08:11:21 -08:00 committed by GitHub
parent b6ef6f69a9
commit 1c3de541e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 452 additions and 53 deletions

View File

@ -130,7 +130,6 @@ creation according to their needs, the :class:`EnvBuilder` class.
.. versionadded:: 3.6
Added the ``prompt`` parameter
Creators of third-party virtual environment tools will be free to use the
provided ``EnvBuilder`` class as a base class.
@ -177,16 +176,15 @@ creation according to their needs, the :class:`EnvBuilder` class.
.. method:: setup_python(context)
Creates a copy of the Python executable (and, under Windows, DLLs) in
the environment. On a POSIX system, if a specific executable
``python3.x`` was used, symlinks to ``python`` and ``python3`` will be
created pointing to that executable, unless files with those names
already exist.
Creates a copy of the Python executable in the environment on POSIX
systems. If a specific executable ``python3.x`` was used, symlinks to
``python`` and ``python3`` will be created pointing to that executable,
unless files with those names already exist.
.. method:: setup_scripts(context)
Installs activation scripts appropriate to the platform into the virtual
environment.
environment. On Windows, also installs the ``python[w].exe`` scripts.
.. method:: post_setup(context)
@ -194,6 +192,11 @@ creation according to their needs, the :class:`EnvBuilder` class.
implementations to pre-install packages in the virtual environment or
perform other post-creation steps.
.. versionchanged:: 3.7.2
Windows now uses redirector scripts for ``python[w].exe`` instead of
copying the actual binaries, and so :meth:`setup_python` does nothing
unless running from a build in the source tree.
In addition, :class:`EnvBuilder` provides this utility method that can be
called from :meth:`setup_scripts` or :meth:`post_setup` in subclasses to
assist in installing custom scripts into the virtual environment.

View File

@ -2512,3 +2512,13 @@ In 3.7.1 the :mod:`tokenize` module now implicitly emits a ``NEWLINE`` token
when provided with input that does not have a trailing new line. This behavior
now matches what the C tokenizer does internally.
(Contributed by Ammar Askar in :issue:`33899`.)
Notable changes in Python 3.7.2
===============================
In 3.7.2, :mod:`venv` on Windows no longer copies the original binaries, but
creates redirector scripts named ``python.exe`` and ``pythonw.exe`` instead.
This resolves a long standing issue where all virtual environments would have
to be upgraded or recreated with each Python update. However, note that this
release will still require recreation of virtual environments in order to get
the new scripts.

View File

@ -243,6 +243,7 @@ class BasicTest(BaseTest):
self.assertIn('include-system-site-packages = %s\n' % s, data)
@unittest.skipUnless(can_symlink(), 'Needs symlinks')
@unittest.skipIf(os.name == 'nt', 'Symlinks are never used on Windows')
def test_symlinking(self):
"""
Test symlinking works as expected

View File

@ -64,10 +64,11 @@ class EnvBuilder:
self.system_site_packages = False
self.create_configuration(context)
self.setup_python(context)
if not self.upgrade:
self.setup_scripts(context)
if self.with_pip:
self._setup_pip(context)
if not self.upgrade:
self.setup_scripts(context)
self.post_setup(context)
if true_system_site_packages:
# We had set it to False before, now
@ -158,14 +159,6 @@ class EnvBuilder:
f.write('include-system-site-packages = %s\n' % incl)
f.write('version = %d.%d.%d\n' % sys.version_info[:3])
if os.name == 'nt':
def include_binary(self, f):
if f.endswith(('.pyd', '.dll')):
result = True
else:
result = f.startswith('python') and f.endswith('.exe')
return result
def symlink_or_copy(self, src, dst, relative_symlinks_ok=False):
"""
Try symlinking a file, and if that fails, fall back to copying.
@ -195,9 +188,9 @@ class EnvBuilder:
binpath = context.bin_path
path = context.env_exe
copier = self.symlink_or_copy
copier(context.executable, path)
dirname = context.python_dir
if os.name != 'nt':
copier(context.executable, path)
if not os.path.islink(path):
os.chmod(path, 0o755)
for suffix in ('python', 'python3'):
@ -209,26 +202,22 @@ class EnvBuilder:
if not os.path.islink(path):
os.chmod(path, 0o755)
else:
# See bpo-34011. When using a proper install, we should only need to
# copy the top-level of DLLs.
include = self.include_binary
files = [f for f in os.listdir(dirname) if include(f)]
for f in files:
src = os.path.join(dirname, f)
dst = os.path.join(binpath, f)
if dst != context.env_exe: # already done, above
# For normal cases, the venvlauncher will be copied from
# our scripts folder. For builds, we need to copy it
# manually.
if sysconfig.is_python_build(True):
suffix = '.exe'
if context.python_exe.lower().endswith('_d.exe'):
suffix = '_d.exe'
src = os.path.join(dirname, "venvlauncher" + suffix)
dst = os.path.join(binpath, context.python_exe)
copier(src, dst)
# When creating from a build directory, we continue to copy all files.
if sysconfig.is_python_build(True):
subdir = 'DLLs'
dirname = os.path.join(dirname, subdir)
if os.path.isdir(dirname):
files = [f for f in os.listdir(dirname) if include(f)]
for f in files:
src = os.path.join(dirname, f)
dst = os.path.join(binpath, f)
src = os.path.join(dirname, "venvwlauncher" + suffix)
dst = os.path.join(binpath, "pythonw" + suffix)
copier(src, dst)
# copy init.tcl over
for root, dirs, files in os.walk(context.python_dir):
if 'init.tcl' in files:
@ -326,7 +315,7 @@ class EnvBuilder:
dstfile = os.path.join(dstdir, f)
with open(srcfile, 'rb') as f:
data = f.read()
if not srcfile.endswith('.exe'):
if not srcfile.endswith(('.exe', '.pdb')):
try:
data = data.decode('utf-8')
data = self.replace_variables(data, context)

View File

@ -0,0 +1,2 @@
venv on Windows will now use a python.exe redirector rather than copying the
actual binaries from the base environment.

View File

@ -536,10 +536,16 @@ static _PyInitError
get_program_full_path(const _PyCoreConfig *core_config,
PyCalculatePath *calculate, _PyPathConfig *config)
{
const wchar_t *pyvenv_launcher;
wchar_t program_full_path[MAXPATHLEN+1];
memset(program_full_path, 0, sizeof(program_full_path));
if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) {
/* The launcher may need to force the executable path to a
* different environment, so override it here. */
pyvenv_launcher = _wgetenv(L"__PYVENV_LAUNCHER__");
if (pyvenv_launcher && pyvenv_launcher[0]) {
wcscpy_s(program_full_path, MAXPATHLEN+1, pyvenv_launcher);
} else if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) {
/* GetModuleFileName should never fail when passed NULL */
return _Py_INIT_ERR("Cannot determine program path");
}

View File

@ -28,7 +28,7 @@
#define RC_NO_PYTHON 103
#define RC_NO_MEMORY 104
/*
* SCRIPT_WRAPPER is used to choose between two variants of an executable built
* SCRIPT_WRAPPER is used to choose one of the variants of an executable built
* from this source file. If not defined, the PEP 397 Python launcher is built;
* if defined, a script launcher of the type used by setuptools is built, which
* looks for a script name related to the executable name and runs that script
@ -40,6 +40,15 @@
#if defined(SCRIPT_WRAPPER)
#define RC_NO_SCRIPT 105
#endif
/*
* VENV_REDIRECT is used to choose the variant that looks for an adjacent or
* one-level-higher pyvenv.cfg, and uses its "home" property to locate and
* launch the original python.exe.
*/
#if defined(VENV_REDIRECT)
#define RC_NO_VENV_CFG 106
#define RC_BAD_VENV_CFG 107
#endif
/* Just for now - static definition */
@ -97,7 +106,7 @@ error(int rc, wchar_t * format, ... )
#if !defined(_WINDOWS)
fwprintf(stderr, L"%ls\n", message);
#else
MessageBox(NULL, message, TEXT("Python Launcher is sorry to say ..."),
MessageBoxW(NULL, message, L"Python Launcher is sorry to say ...",
MB_OK);
#endif
exit(rc);
@ -131,6 +140,17 @@ static wchar_t * get_env(wchar_t * key)
return buf;
}
#if defined(_DEBUG)
#if defined(_WINDOWS)
#define PYTHON_EXECUTABLE L"pythonw_d.exe"
#else
#define PYTHON_EXECUTABLE L"python_d.exe"
#endif
#else
#if defined(_WINDOWS)
#define PYTHON_EXECUTABLE L"pythonw.exe"
@ -139,6 +159,7 @@ static wchar_t * get_env(wchar_t * key)
#define PYTHON_EXECUTABLE L"python.exe"
#endif
#endif
#define MAX_VERSION_SIZE 4
@ -1457,6 +1478,87 @@ show_python_list(wchar_t ** argv)
return FALSE; /* If this has been called we cannot continue */
}
#if defined(VENV_REDIRECT)
static int
find_home_value(const char *buffer, const char **start, DWORD *length)
{
for (const char *s = strstr(buffer, "home"); s; s = strstr(s + 1, "\nhome")) {
if (*s == '\n') {
++s;
}
for (int i = 4; i > 0 && *s; --i, ++s);
while (*s && iswspace(*s)) {
++s;
}
if (*s != L'=') {
continue;
}
do {
++s;
} while (*s && iswspace(*s));
*start = s;
char *nl = strchr(s, '\n');
if (nl) {
*length = (DWORD)((ptrdiff_t)nl - (ptrdiff_t)s);
} else {
*length = (DWORD)strlen(s);
}
return 1;
}
return 0;
}
#endif
static wchar_t *
wcsdup_pad(const wchar_t *s, int padding, int *newlen)
{
size_t len = wcslen(s);
len += 1 + padding;
wchar_t *r = (wchar_t *)malloc(len * sizeof(wchar_t));
if (!r) {
return NULL;
}
if (wcscpy_s(r, len, s)) {
free(r);
return NULL;
}
*newlen = len < MAXINT ? (int)len : MAXINT;
return r;
}
static wchar_t *
get_process_name()
{
DWORD bufferLen = MAX_PATH;
DWORD len = bufferLen;
wchar_t *r = NULL;
while (!r) {
r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t));
if (!r) {
error(RC_NO_MEMORY, L"out of memory");
return NULL;
}
len = GetModuleFileNameW(NULL, r, bufferLen);
if (len == 0) {
free(r);
error(0, L"Failed to get module name");
return NULL;
} else if (len == bufferLen &&
GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
free(r);
r = NULL;
bufferLen *= 2;
}
}
return r;
}
static int
process(int argc, wchar_t ** argv)
{
@ -1464,21 +1566,27 @@ process(int argc, wchar_t ** argv)
wchar_t * command;
wchar_t * executable;
wchar_t * p;
wchar_t * argv0;
int rc = 0;
size_t plen;
INSTALLED_PYTHON * ip;
BOOL valid;
DWORD size, attrs;
HRESULT hr;
wchar_t message[MSGSIZE];
void * version_data;
VS_FIXEDFILEINFO * file_info;
UINT block_size;
int index;
#if defined(SCRIPT_WRAPPER)
#if defined(VENV_REDIRECT)
wchar_t * venv_cfg_path;
int newlen;
#elif defined(SCRIPT_WRAPPER)
wchar_t * newcommand;
wchar_t * av[2];
int newlen;
HRESULT hr;
int index;
#else
HRESULT hr;
int index;
#endif
setvbuf(stderr, (char *)NULL, _IONBF, 0);
@ -1496,6 +1604,7 @@ process(int argc, wchar_t ** argv)
#else
debug(L"launcher executable: Console\n");
#endif
#if !defined(VENV_REDIRECT)
/* Get the local appdata folder (non-roaming) */
hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA,
NULL, 0, appdata_ini_path);
@ -1504,9 +1613,7 @@ process(int argc, wchar_t ** argv)
appdata_ini_path[0] = L'\0';
}
else {
plen = wcslen(appdata_ini_path);
p = &appdata_ini_path[plen];
wcsncpy_s(p, MAX_PATH - plen, L"\\py.ini", _TRUNCATE);
wcsncat_s(appdata_ini_path, MAX_PATH, L"\\py.ini", _TRUNCATE);
attrs = GetFileAttributesW(appdata_ini_path);
if (attrs == INVALID_FILE_ATTRIBUTES) {
debug(L"File '%ls' non-existent\n", appdata_ini_path);
@ -1515,8 +1622,9 @@ process(int argc, wchar_t ** argv)
debug(L"Using local configuration file '%ls'\n", appdata_ini_path);
}
}
plen = GetModuleFileNameW(NULL, launcher_ini_path, MAX_PATH);
size = GetFileVersionInfoSizeW(launcher_ini_path, &size);
#endif
argv0 = get_process_name();
size = GetFileVersionInfoSizeW(argv0, &size);
if (size == 0) {
winerror(GetLastError(), message, MSGSIZE);
debug(L"GetFileVersionInfoSize failed: %ls\n", message);
@ -1524,7 +1632,7 @@ process(int argc, wchar_t ** argv)
else {
version_data = malloc(size);
if (version_data) {
valid = GetFileVersionInfoW(launcher_ini_path, 0, size,
valid = GetFileVersionInfoW(argv0, 0, size,
version_data);
if (!valid)
debug(L"GetFileVersionInfo failed: %X\n", GetLastError());
@ -1541,15 +1649,51 @@ process(int argc, wchar_t ** argv)
free(version_data);
}
}
#if defined(VENV_REDIRECT)
/* Allocate some extra space for new filenames */
venv_cfg_path = wcsdup_pad(argv0, 32, &newlen);
if (!venv_cfg_path) {
error(RC_NO_MEMORY, L"Failed to copy module name");
}
p = wcsrchr(venv_cfg_path, L'\\');
if (p == NULL) {
error(RC_NO_VENV_CFG, L"No pyvenv.cfg file");
}
p[0] = L'\0';
wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg");
attrs = GetFileAttributesW(venv_cfg_path);
if (attrs == INVALID_FILE_ATTRIBUTES) {
debug(L"File '%ls' non-existent\n", venv_cfg_path);
p[0] = '\0';
p = wcsrchr(venv_cfg_path, L'\\');
if (p != NULL) {
p[0] = '\0';
wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg");
attrs = GetFileAttributesW(venv_cfg_path);
if (attrs == INVALID_FILE_ATTRIBUTES) {
debug(L"File '%ls' non-existent\n", venv_cfg_path);
error(RC_NO_VENV_CFG, L"No pyvenv.cfg file");
}
}
}
debug(L"Using venv configuration file '%ls'\n", venv_cfg_path);
#else
/* Allocate some extra space for new filenames */
if (wcscpy_s(launcher_ini_path, MAX_PATH, argv0)) {
error(RC_NO_MEMORY, L"Failed to copy module name");
}
p = wcsrchr(launcher_ini_path, L'\\');
if (p == NULL) {
debug(L"GetModuleFileNameW returned value has no backslash: %ls\n",
launcher_ini_path);
launcher_ini_path[0] = L'\0';
}
else {
wcsncpy_s(p, MAX_PATH - (p - launcher_ini_path), L"\\py.ini",
_TRUNCATE);
p[0] = L'\0';
wcscat_s(launcher_ini_path, MAX_PATH, L"\\py.ini");
attrs = GetFileAttributesW(launcher_ini_path);
if (attrs == INVALID_FILE_ATTRIBUTES) {
debug(L"File '%ls' non-existent\n", launcher_ini_path);
@ -1558,6 +1702,7 @@ process(int argc, wchar_t ** argv)
debug(L"Using global configuration file '%ls'\n", launcher_ini_path);
}
}
#endif
command = skip_me(GetCommandLineW());
debug(L"Called with command line: %ls\n", command);
@ -1593,6 +1738,55 @@ process(int argc, wchar_t ** argv)
command = newcommand;
valid = FALSE;
}
#elif defined(VENV_REDIRECT)
{
FILE *f;
char buffer[4096]; /* 4KB should be enough for anybody */
char *start;
DWORD len, cch, cch_actual;
size_t cb;
if (_wfopen_s(&f, venv_cfg_path, L"r")) {
error(RC_BAD_VENV_CFG, L"Cannot read '%ls'", venv_cfg_path);
}
cb = fread_s(buffer, sizeof(buffer), sizeof(buffer[0]),
sizeof(buffer) / sizeof(buffer[0]), f);
fclose(f);
if (!find_home_value(buffer, &start, &len)) {
error(RC_BAD_VENV_CFG, L"Cannot find home in '%ls'",
venv_cfg_path);
}
cch = MultiByteToWideChar(CP_UTF8, 0, start, len, NULL, 0);
if (!cch) {
error(0, L"Cannot determine memory for home path");
}
cch += (DWORD)wcslen(PYTHON_EXECUTABLE) + 1 + 1; /* include sep and null */
executable = (wchar_t *)malloc(cch * sizeof(wchar_t));
if (executable == NULL) {
error(RC_NO_MEMORY, L"A memory allocation failed");
}
cch_actual = MultiByteToWideChar(CP_UTF8, 0, start, len, executable, cch);
if (!cch_actual) {
error(RC_BAD_VENV_CFG, L"Cannot decode home path in '%ls'",
venv_cfg_path);
}
if (executable[cch_actual - 1] != L'\\') {
executable[cch_actual++] = L'\\';
executable[cch_actual] = L'\0';
}
if (wcscat_s(executable, cch, PYTHON_EXECUTABLE)) {
error(RC_BAD_VENV_CFG, L"Cannot create executable path from '%ls'",
venv_cfg_path);
}
if (GetFileAttributesW(executable) == INVALID_FILE_ATTRIBUTES) {
error(RC_NO_PYTHON, L"No Python at '%ls'", executable);
}
if (!SetEnvironmentVariableW(L"__PYVENV_LAUNCHER__", argv0)) {
error(0, L"Failed to set launcher environment");
}
valid = 1;
}
#else
if (argc <= 1) {
valid = FALSE;
@ -1600,7 +1794,6 @@ process(int argc, wchar_t ** argv)
}
else {
p = argv[1];
plen = wcslen(p);
if ((argc == 2) && // list version args
(!wcsncmp(p, L"-0", wcslen(L"-0")) ||
!wcsncmp(p, L"--list", wcslen(L"--list"))))

View File

@ -70,6 +70,8 @@
<Projects2 Include="_freeze_importlib.vcxproj" />
<!-- python[w].exe -->
<Projects2 Include="python.vcxproj;pythonw.vcxproj" />
<!-- venv[w]launcher.exe -->
<Projects2 Include="venvlauncher.vcxproj;venvwlauncher.vcxproj" />
</ItemGroup>
<Target Name="Build">

View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="PGInstrument|Win32">
<Configuration>PGInstrument</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="PGInstrument|x64">
<Configuration>PGInstrument</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="PGUpdate|Win32">
<Configuration>PGUpdate</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="PGUpdate|x64">
<Configuration>PGUpdate</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}</ProjectGuid>
<RootNamespace>venvlauncher</RootNamespace>
<TargetName>venvlauncher</TargetName>
<SupportPGO>false</SupportPGO>
</PropertyGroup>
<Import Project="python.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<PropertyGroup>
<MakeVersionInfoBeforeTarget>ClCompile</MakeVersionInfoBeforeTarget>
</PropertyGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="pyproject.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>_CONSOLE;VENV_REDIRECT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>PY_ICON;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<Link>
<AdditionalDependencies>version.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\PC\launcher.c" />
</ItemGroup>
<ItemGroup>
<None Include="..\PC\launcher.ico" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\PC\pylauncher.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="PGInstrument|Win32">
<Configuration>PGInstrument</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="PGInstrument|x64">
<Configuration>PGInstrument</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="PGUpdate|Win32">
<Configuration>PGUpdate</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="PGUpdate|x64">
<Configuration>PGUpdate</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}</ProjectGuid>
<RootNamespace>venvwlauncher</RootNamespace>
<TargetName>venvwlauncher</TargetName>
<SupportPGO>false</SupportPGO>
</PropertyGroup>
<Import Project="python.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<PropertyGroup>
<MakeVersionInfoBeforeTarget>ClCompile</MakeVersionInfoBeforeTarget>
</PropertyGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="pyproject.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>_WINDOWS;VENV_REDIRECT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>PYW_ICON;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<Link>
<AdditionalDependencies>version.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\PC\launcher.c" />
</ItemGroup>
<ItemGroup>
<None Include="..\PC\launcher.ico" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\PC\pylauncher.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -2,6 +2,8 @@
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define exts=pyexpat;select;unicodedata;winsound;_bz2;_elementtree;_socket;_ssl;_msi;_ctypes;_hashlib;_multiprocessing;_lzma;_decimal;_overlapped;_sqlite3;_asyncio;_queue;_contextvars ?>
<Fragment>
<DirectoryRef Id="Lib_venv_scripts_nt" />
<ComponentGroup Id="lib_extensions">
<?foreach ext in $(var.exts)?>
@ -20,10 +22,25 @@
<Component Id="libssl.dll" Directory="DLLs" Guid="*">
<File Name="libssl$(var.ssltag).dll" KeyPath="yes" />
</Component>
<Component Id="venvlauncher.exe" Directory="Lib_venv_scripts_nt" Guid="*">
<File Name="python.exe" Source="venvlauncher.exe" KeyPath="yes" />
</Component>
<Component Id="venvwlauncher.exe" Directory="Lib_venv_scripts_nt" Guid="*">
<File Name="pythonw.exe" Source="venvwlauncher.exe" KeyPath="yes" />
</Component>
</ComponentGroup>
</Fragment>
<Fragment>
<!-- The auto-generated directory is not available when building symbols -->
<DirectoryRef Id="Lib">
<Directory Id="Lib_venv__pdbs" Name="venv">
<Directory Id="Lib_venv_scripts__pdbs" Name="scripts">
<Directory Id="Lib_venv_scripts_nt__pdbs" Name="nt" />
</Directory>
</Directory>
</DirectoryRef>
<ComponentGroup Id="lib_extensions_symbols">
<?foreach ext in $(var.exts)?>
@ -42,6 +59,12 @@
<Component Id="libssl.pdb" Directory="DLLs" Guid="*">
<File Name="libssl$(var.ssltag).pdb" KeyPath="yes" />
</Component>
<Component Id="venvlauncher.pdb" Directory="Lib_venv_scripts_nt__pdbs" Guid="*">
<File Name="python.pdb" Source="venvlauncher.pdb" KeyPath="yes" />
</Component>
<Component Id="venvwlauncher.pdb" Directory="Lib_venv_scripts_nt__pdbs" Guid="*">
<File Name="pythonw.pdb" Source="venvwlauncher.pdb" KeyPath="yes" />
</Component>
</ComponentGroup>
</Fragment>