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 .. versionadded:: 3.6
Added the ``prompt`` parameter Added the ``prompt`` parameter
Creators of third-party virtual environment tools will be free to use the Creators of third-party virtual environment tools will be free to use the
provided ``EnvBuilder`` class as a base class. provided ``EnvBuilder`` class as a base class.
@ -177,16 +176,15 @@ creation according to their needs, the :class:`EnvBuilder` class.
.. method:: setup_python(context) .. method:: setup_python(context)
Creates a copy of the Python executable (and, under Windows, DLLs) in Creates a copy of the Python executable in the environment on POSIX
the environment. On a POSIX system, if a specific executable systems. If a specific executable ``python3.x`` was used, symlinks to
``python3.x`` was used, symlinks to ``python`` and ``python3`` will be ``python`` and ``python3`` will be created pointing to that executable,
created pointing to that executable, unless files with those names unless files with those names already exist.
already exist.
.. method:: setup_scripts(context) .. method:: setup_scripts(context)
Installs activation scripts appropriate to the platform into the virtual 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) .. 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 implementations to pre-install packages in the virtual environment or
perform other post-creation steps. 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 In addition, :class:`EnvBuilder` provides this utility method that can be
called from :meth:`setup_scripts` or :meth:`post_setup` in subclasses to called from :meth:`setup_scripts` or :meth:`post_setup` in subclasses to
assist in installing custom scripts into the virtual environment. 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 when provided with input that does not have a trailing new line. This behavior
now matches what the C tokenizer does internally. now matches what the C tokenizer does internally.
(Contributed by Ammar Askar in :issue:`33899`.) (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) self.assertIn('include-system-site-packages = %s\n' % s, data)
@unittest.skipUnless(can_symlink(), 'Needs symlinks') @unittest.skipUnless(can_symlink(), 'Needs symlinks')
@unittest.skipIf(os.name == 'nt', 'Symlinks are never used on Windows')
def test_symlinking(self): def test_symlinking(self):
""" """
Test symlinking works as expected Test symlinking works as expected

View File

@ -64,10 +64,11 @@ class EnvBuilder:
self.system_site_packages = False self.system_site_packages = False
self.create_configuration(context) self.create_configuration(context)
self.setup_python(context) self.setup_python(context)
if not self.upgrade:
self.setup_scripts(context)
if self.with_pip: if self.with_pip:
self._setup_pip(context) self._setup_pip(context)
if not self.upgrade: if not self.upgrade:
self.setup_scripts(context)
self.post_setup(context) self.post_setup(context)
if true_system_site_packages: if true_system_site_packages:
# We had set it to False before, now # 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('include-system-site-packages = %s\n' % incl)
f.write('version = %d.%d.%d\n' % sys.version_info[:3]) 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): def symlink_or_copy(self, src, dst, relative_symlinks_ok=False):
""" """
Try symlinking a file, and if that fails, fall back to copying. Try symlinking a file, and if that fails, fall back to copying.
@ -195,9 +188,9 @@ class EnvBuilder:
binpath = context.bin_path binpath = context.bin_path
path = context.env_exe path = context.env_exe
copier = self.symlink_or_copy copier = self.symlink_or_copy
copier(context.executable, path)
dirname = context.python_dir dirname = context.python_dir
if os.name != 'nt': if os.name != 'nt':
copier(context.executable, path)
if not os.path.islink(path): if not os.path.islink(path):
os.chmod(path, 0o755) os.chmod(path, 0o755)
for suffix in ('python', 'python3'): for suffix in ('python', 'python3'):
@ -209,26 +202,22 @@ class EnvBuilder:
if not os.path.islink(path): if not os.path.islink(path):
os.chmod(path, 0o755) os.chmod(path, 0o755)
else: else:
# See bpo-34011. When using a proper install, we should only need to # For normal cases, the venvlauncher will be copied from
# copy the top-level of DLLs. # our scripts folder. For builds, we need to copy it
include = self.include_binary # manually.
files = [f for f in os.listdir(dirname) if include(f)] if sysconfig.is_python_build(True):
for f in files: suffix = '.exe'
src = os.path.join(dirname, f) if context.python_exe.lower().endswith('_d.exe'):
dst = os.path.join(binpath, f) suffix = '_d.exe'
if dst != context.env_exe: # already done, above
src = os.path.join(dirname, "venvlauncher" + suffix)
dst = os.path.join(binpath, context.python_exe)
copier(src, dst) copier(src, dst)
# When creating from a build directory, we continue to copy all files. src = os.path.join(dirname, "venvwlauncher" + suffix)
if sysconfig.is_python_build(True): dst = os.path.join(binpath, "pythonw" + suffix)
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)
copier(src, dst) copier(src, dst)
# copy init.tcl over # copy init.tcl over
for root, dirs, files in os.walk(context.python_dir): for root, dirs, files in os.walk(context.python_dir):
if 'init.tcl' in files: if 'init.tcl' in files:
@ -326,7 +315,7 @@ class EnvBuilder:
dstfile = os.path.join(dstdir, f) dstfile = os.path.join(dstdir, f)
with open(srcfile, 'rb') as f: with open(srcfile, 'rb') as f:
data = f.read() data = f.read()
if not srcfile.endswith('.exe'): if not srcfile.endswith(('.exe', '.pdb')):
try: try:
data = data.decode('utf-8') data = data.decode('utf-8')
data = self.replace_variables(data, context) 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, get_program_full_path(const _PyCoreConfig *core_config,
PyCalculatePath *calculate, _PyPathConfig *config) PyCalculatePath *calculate, _PyPathConfig *config)
{ {
const wchar_t *pyvenv_launcher;
wchar_t program_full_path[MAXPATHLEN+1]; wchar_t program_full_path[MAXPATHLEN+1];
memset(program_full_path, 0, sizeof(program_full_path)); 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 */ /* GetModuleFileName should never fail when passed NULL */
return _Py_INIT_ERR("Cannot determine program path"); return _Py_INIT_ERR("Cannot determine program path");
} }

View File

@ -28,7 +28,7 @@
#define RC_NO_PYTHON 103 #define RC_NO_PYTHON 103
#define RC_NO_MEMORY 104 #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; * 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 * 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 * looks for a script name related to the executable name and runs that script
@ -40,6 +40,15 @@
#if defined(SCRIPT_WRAPPER) #if defined(SCRIPT_WRAPPER)
#define RC_NO_SCRIPT 105 #define RC_NO_SCRIPT 105
#endif #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 */ /* Just for now - static definition */
@ -97,7 +106,7 @@ error(int rc, wchar_t * format, ... )
#if !defined(_WINDOWS) #if !defined(_WINDOWS)
fwprintf(stderr, L"%ls\n", message); fwprintf(stderr, L"%ls\n", message);
#else #else
MessageBox(NULL, message, TEXT("Python Launcher is sorry to say ..."), MessageBoxW(NULL, message, L"Python Launcher is sorry to say ...",
MB_OK); MB_OK);
#endif #endif
exit(rc); exit(rc);
@ -131,6 +140,17 @@ static wchar_t * get_env(wchar_t * key)
return buf; 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) #if defined(_WINDOWS)
#define PYTHON_EXECUTABLE L"pythonw.exe" #define PYTHON_EXECUTABLE L"pythonw.exe"
@ -139,6 +159,7 @@ static wchar_t * get_env(wchar_t * key)
#define PYTHON_EXECUTABLE L"python.exe" #define PYTHON_EXECUTABLE L"python.exe"
#endif
#endif #endif
#define MAX_VERSION_SIZE 4 #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 */ 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 static int
process(int argc, wchar_t ** argv) process(int argc, wchar_t ** argv)
{ {
@ -1464,21 +1566,27 @@ process(int argc, wchar_t ** argv)
wchar_t * command; wchar_t * command;
wchar_t * executable; wchar_t * executable;
wchar_t * p; wchar_t * p;
wchar_t * argv0;
int rc = 0; int rc = 0;
size_t plen;
INSTALLED_PYTHON * ip; INSTALLED_PYTHON * ip;
BOOL valid; BOOL valid;
DWORD size, attrs; DWORD size, attrs;
HRESULT hr;
wchar_t message[MSGSIZE]; wchar_t message[MSGSIZE];
void * version_data; void * version_data;
VS_FIXEDFILEINFO * file_info; VS_FIXEDFILEINFO * file_info;
UINT block_size; UINT block_size;
int index; #if defined(VENV_REDIRECT)
#if defined(SCRIPT_WRAPPER) wchar_t * venv_cfg_path;
int newlen; int newlen;
#elif defined(SCRIPT_WRAPPER)
wchar_t * newcommand; wchar_t * newcommand;
wchar_t * av[2]; wchar_t * av[2];
int newlen;
HRESULT hr;
int index;
#else
HRESULT hr;
int index;
#endif #endif
setvbuf(stderr, (char *)NULL, _IONBF, 0); setvbuf(stderr, (char *)NULL, _IONBF, 0);
@ -1496,6 +1604,7 @@ process(int argc, wchar_t ** argv)
#else #else
debug(L"launcher executable: Console\n"); debug(L"launcher executable: Console\n");
#endif #endif
#if !defined(VENV_REDIRECT)
/* Get the local appdata folder (non-roaming) */ /* Get the local appdata folder (non-roaming) */
hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA,
NULL, 0, appdata_ini_path); NULL, 0, appdata_ini_path);
@ -1504,9 +1613,7 @@ process(int argc, wchar_t ** argv)
appdata_ini_path[0] = L'\0'; appdata_ini_path[0] = L'\0';
} }
else { else {
plen = wcslen(appdata_ini_path); wcsncat_s(appdata_ini_path, MAX_PATH, L"\\py.ini", _TRUNCATE);
p = &appdata_ini_path[plen];
wcsncpy_s(p, MAX_PATH - plen, L"\\py.ini", _TRUNCATE);
attrs = GetFileAttributesW(appdata_ini_path); attrs = GetFileAttributesW(appdata_ini_path);
if (attrs == INVALID_FILE_ATTRIBUTES) { if (attrs == INVALID_FILE_ATTRIBUTES) {
debug(L"File '%ls' non-existent\n", appdata_ini_path); 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); debug(L"Using local configuration file '%ls'\n", appdata_ini_path);
} }
} }
plen = GetModuleFileNameW(NULL, launcher_ini_path, MAX_PATH); #endif
size = GetFileVersionInfoSizeW(launcher_ini_path, &size); argv0 = get_process_name();
size = GetFileVersionInfoSizeW(argv0, &size);
if (size == 0) { if (size == 0) {
winerror(GetLastError(), message, MSGSIZE); winerror(GetLastError(), message, MSGSIZE);
debug(L"GetFileVersionInfoSize failed: %ls\n", message); debug(L"GetFileVersionInfoSize failed: %ls\n", message);
@ -1524,7 +1632,7 @@ process(int argc, wchar_t ** argv)
else { else {
version_data = malloc(size); version_data = malloc(size);
if (version_data) { if (version_data) {
valid = GetFileVersionInfoW(launcher_ini_path, 0, size, valid = GetFileVersionInfoW(argv0, 0, size,
version_data); version_data);
if (!valid) if (!valid)
debug(L"GetFileVersionInfo failed: %X\n", GetLastError()); debug(L"GetFileVersionInfo failed: %X\n", GetLastError());
@ -1541,15 +1649,51 @@ process(int argc, wchar_t ** argv)
free(version_data); 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'\\'); p = wcsrchr(launcher_ini_path, L'\\');
if (p == NULL) { if (p == NULL) {
debug(L"GetModuleFileNameW returned value has no backslash: %ls\n", debug(L"GetModuleFileNameW returned value has no backslash: %ls\n",
launcher_ini_path); launcher_ini_path);
launcher_ini_path[0] = L'\0'; launcher_ini_path[0] = L'\0';
} }
else { else {
wcsncpy_s(p, MAX_PATH - (p - launcher_ini_path), L"\\py.ini", p[0] = L'\0';
_TRUNCATE); wcscat_s(launcher_ini_path, MAX_PATH, L"\\py.ini");
attrs = GetFileAttributesW(launcher_ini_path); attrs = GetFileAttributesW(launcher_ini_path);
if (attrs == INVALID_FILE_ATTRIBUTES) { if (attrs == INVALID_FILE_ATTRIBUTES) {
debug(L"File '%ls' non-existent\n", launcher_ini_path); 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); debug(L"Using global configuration file '%ls'\n", launcher_ini_path);
} }
} }
#endif
command = skip_me(GetCommandLineW()); command = skip_me(GetCommandLineW());
debug(L"Called with command line: %ls\n", command); debug(L"Called with command line: %ls\n", command);
@ -1593,6 +1738,55 @@ process(int argc, wchar_t ** argv)
command = newcommand; command = newcommand;
valid = FALSE; 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 #else
if (argc <= 1) { if (argc <= 1) {
valid = FALSE; valid = FALSE;
@ -1600,7 +1794,6 @@ process(int argc, wchar_t ** argv)
} }
else { else {
p = argv[1]; p = argv[1];
plen = wcslen(p);
if ((argc == 2) && // list version args if ((argc == 2) && // list version args
(!wcsncmp(p, L"-0", wcslen(L"-0")) || (!wcsncmp(p, L"-0", wcslen(L"-0")) ||
!wcsncmp(p, L"--list", wcslen(L"--list")))) !wcsncmp(p, L"--list", wcslen(L"--list"))))

View File

@ -70,6 +70,8 @@
<Projects2 Include="_freeze_importlib.vcxproj" /> <Projects2 Include="_freeze_importlib.vcxproj" />
<!-- python[w].exe --> <!-- python[w].exe -->
<Projects2 Include="python.vcxproj;pythonw.vcxproj" /> <Projects2 Include="python.vcxproj;pythonw.vcxproj" />
<!-- venv[w]launcher.exe -->
<Projects2 Include="venvlauncher.vcxproj;venvwlauncher.vcxproj" />
</ItemGroup> </ItemGroup>
<Target Name="Build"> <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"> <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 ?> <?define exts=pyexpat;select;unicodedata;winsound;_bz2;_elementtree;_socket;_ssl;_msi;_ctypes;_hashlib;_multiprocessing;_lzma;_decimal;_overlapped;_sqlite3;_asyncio;_queue;_contextvars ?>
<Fragment> <Fragment>
<DirectoryRef Id="Lib_venv_scripts_nt" />
<ComponentGroup Id="lib_extensions"> <ComponentGroup Id="lib_extensions">
<?foreach ext in $(var.exts)?> <?foreach ext in $(var.exts)?>
@ -20,10 +22,25 @@
<Component Id="libssl.dll" Directory="DLLs" Guid="*"> <Component Id="libssl.dll" Directory="DLLs" Guid="*">
<File Name="libssl$(var.ssltag).dll" KeyPath="yes" /> <File Name="libssl$(var.ssltag).dll" KeyPath="yes" />
</Component> </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> </ComponentGroup>
</Fragment> </Fragment>
<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"> <ComponentGroup Id="lib_extensions_symbols">
<?foreach ext in $(var.exts)?> <?foreach ext in $(var.exts)?>
@ -42,6 +59,12 @@
<Component Id="libssl.pdb" Directory="DLLs" Guid="*"> <Component Id="libssl.pdb" Directory="DLLs" Guid="*">
<File Name="libssl$(var.ssltag).pdb" KeyPath="yes" /> <File Name="libssl$(var.ssltag).pdb" KeyPath="yes" />
</Component> </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> </ComponentGroup>
</Fragment> </Fragment>