bpo-34977: Add Windows App Store package (GH-10245)

This commit is contained in:
Steve Dower 2018-12-06 21:09:20 -08:00 committed by GitHub
parent c9566b8c45
commit 468a15aaf9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 3082 additions and 331 deletions

View File

@ -0,0 +1,65 @@
jobs:
- job: Prebuild
displayName: Pre-build checks
pool:
vmImage: ubuntu-16.04
steps:
- template: ./prebuild-checks.yml
- job: Windows_Appx_Tests
displayName: Windows Appx Tests
dependsOn: Prebuild
condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true'))
pool:
vmImage: vs2017-win2016
strategy:
matrix:
win64:
arch: amd64
buildOpt: '-p x64'
testRunTitle: '$(Build.SourceBranchName)-win64-appx'
testRunPlatform: win64
maxParallel: 2
steps:
- checkout: self
clean: true
fetchDepth: 5
- powershell: |
# Relocate build outputs outside of source directory to make cleaning faster
Write-Host '##vso[task.setvariable variable=Py_IntDir]$(Build.BinariesDirectory)\obj'
# UNDONE: Do not build to a different directory because of broken tests
Write-Host '##vso[task.setvariable variable=Py_OutDir]$(Build.SourcesDirectory)\PCbuild'
Write-Host '##vso[task.setvariable variable=EXTERNAL_DIR]$(Build.BinariesDirectory)\externals'
displayName: Update build locations
- script: PCbuild\build.bat -e $(buildOpt)
displayName: 'Build CPython'
- script: python.bat PC\layout -vv -s "$(Build.SourcesDirectory)" -b "$(Py_OutDir)\$(arch)" -t "$(Py_IntDir)\layout-tmp-$(arch)" --copy "$(Py_IntDir)\layout-$(arch)" --precompile --preset-appx --include-tests
displayName: 'Create APPX layout'
- script: .\python.exe -m test.pythoninfo
workingDirectory: $(Py_IntDir)\layout-$(arch)
displayName: 'Display build info'
- script: .\python.exe -m test -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 --junit-xml="$(Build.BinariesDirectory)\test-results.xml" --tempdir "$(Py_IntDir)\tmp-$(arch)"
workingDirectory: $(Py_IntDir)\layout-$(arch)
displayName: 'Tests'
env:
PREFIX: $(Py_IntDir)\layout-$(arch)
- task: PublishTestResults@2
displayName: 'Publish Test Results'
inputs:
testResultsFiles: '$(Build.BinariesDirectory)\test-results.xml'
mergeTestResults: true
testRunTitle: $(testRunTitle)
platform: $(testRunPlatform)
condition: succeededOrFailed()

1
.gitattributes vendored
View File

@ -19,6 +19,7 @@
# Specific binary files # Specific binary files
Lib/test/sndhdrdata/sndhdr.* binary Lib/test/sndhdrdata/sndhdr.* binary
PC/classicAppCompat.* binary
# Text files that should not be subject to eol conversion # Text files that should not be subject to eol conversion
Lib/test/cjkencodings/* -text Lib/test/cjkencodings/* -text

View File

@ -117,10 +117,12 @@ if not exist "%BUILDDIR%" mkdir "%BUILDDIR%"
if exist ..\Misc\NEWS ( if exist ..\Misc\NEWS (
echo.Copying Misc\NEWS to build\NEWS echo.Copying Misc\NEWS to build\NEWS
if not exist build mkdir build
copy ..\Misc\NEWS build\NEWS > nul copy ..\Misc\NEWS build\NEWS > nul
) else if exist ..\Misc\NEWS.D ( ) else if exist ..\Misc\NEWS.D (
if defined BLURB ( if defined BLURB (
echo.Merging Misc/NEWS with %BLURB% echo.Merging Misc/NEWS with %BLURB%
if not exist build mkdir build
%BLURB% merge -f build\NEWS %BLURB% merge -f build\NEWS
) else ( ) else (
echo.No Misc/NEWS file and Blurb is not available. echo.No Misc/NEWS file and Blurb is not available.

View File

@ -1521,7 +1521,7 @@ class _BasePathTest(object):
# resolves to 'dirB/..' first before resolving to parent of dirB. # resolves to 'dirB/..' first before resolving to parent of dirB.
self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False)
# Now create absolute symlinks # Now create absolute symlinks
d = support._longpath(tempfile.mkdtemp(suffix='-dirD')) d = support._longpath(tempfile.mkdtemp(suffix='-dirD', dir=os.getcwd()))
self.addCleanup(support.rmtree, d) self.addCleanup(support.rmtree, d)
os.symlink(os.path.join(d), join('dirA', 'linkX')) os.symlink(os.path.join(d), join('dirA', 'linkX'))
os.symlink(join('dirB'), os.path.join(d, 'linkY')) os.symlink(join('dirB'), os.path.join(d, 'linkY'))

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 @@
Adds support for building a Windows App Store package

View File

@ -0,0 +1 @@
<CustomCapabilityDescriptor xmlns="http://schemas.microsoft.com/appx/2016/sccd" xmlns:s="http://schemas.microsoft.com/appx/2016/sccd"><CustomCapabilities><CustomCapability Name="Microsoft.classicAppCompat_8wekyb3d8bbwe"></CustomCapability></CustomCapabilities><AuthorizedEntities><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.14_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.15_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.14_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.15_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity></AuthorizedEntities></CustomCapabilityDescriptor>

BIN
PC/classicAppCompat.cat Normal file

Binary file not shown.

28
PC/classicAppCompat.sccd Normal file

File diff suppressed because one or more lines are too long

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");
} }

BIN
PC/icons/pythonwx150.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
PC/icons/pythonwx44.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
PC/icons/pythonx150.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
PC/icons/pythonx44.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
PC/icons/pythonx50.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

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,52 @@ 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));
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 +1791,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"))))

0
PC/layout/__init__.py Normal file
View File

14
PC/layout/__main__.py Normal file
View File

@ -0,0 +1,14 @@
import sys
try:
import layout
except ImportError:
# Failed to import our package, which likely means we were started directly
# Add the additional search path needed to locate our module.
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from layout.main import main
sys.exit(int(main() or 0))

612
PC/layout/main.py Normal file
View File

@ -0,0 +1,612 @@
"""
Generates a layout of Python for Windows from a build.
See python make_layout.py --help for usage.
"""
__author__ = "Steve Dower <steve.dower@python.org>"
__version__ = "3.8"
import argparse
import functools
import os
import re
import shutil
import subprocess
import sys
import tempfile
import zipfile
from pathlib import Path
if __name__ == "__main__":
# Started directly, so enable relative imports
__path__ = [str(Path(__file__).resolve().parent)]
from .support.appxmanifest import *
from .support.catalog import *
from .support.constants import *
from .support.filesets import *
from .support.logging import *
from .support.options import *
from .support.pip import *
from .support.props import *
BDIST_WININST_FILES_ONLY = FileNameSet("wininst-*", "bdist_wininst.py")
BDIST_WININST_STUB = "PC/layout/support/distutils.command.bdist_wininst.py"
TEST_PYDS_ONLY = FileStemSet("xxlimited", "_ctypes_test", "_test*")
TEST_DIRS_ONLY = FileNameSet("test", "tests")
IDLE_DIRS_ONLY = FileNameSet("idlelib")
TCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter")
TCLTK_DIRS_ONLY = FileNameSet("tkinter", "turtledemo")
TCLTK_FILES_ONLY = FileNameSet("turtle.py")
VENV_DIRS_ONLY = FileNameSet("venv", "ensurepip")
EXCLUDE_FROM_PYDS = FileStemSet("python*", "pyshellext")
EXCLUDE_FROM_LIB = FileNameSet("*.pyc", "__pycache__", "*.pickle")
EXCLUDE_FROM_PACKAGED_LIB = FileNameSet("readme.txt")
EXCLUDE_FROM_COMPILE = FileNameSet("badsyntax_*", "bad_*")
EXCLUDE_FROM_CATALOG = FileSuffixSet(".exe", ".pyd", ".dll")
REQUIRED_DLLS = FileStemSet("libcrypto*", "libssl*")
LIB2TO3_GRAMMAR_FILES = FileNameSet("Grammar.txt", "PatternGrammar.txt")
PY_FILES = FileSuffixSet(".py")
PYC_FILES = FileSuffixSet(".pyc")
CAT_FILES = FileSuffixSet(".cat")
CDF_FILES = FileSuffixSet(".cdf")
DATA_DIRS = FileNameSet("data")
TOOLS_DIRS = FileNameSet("scripts", "i18n", "pynche", "demo", "parser")
TOOLS_FILES = FileSuffixSet(".py", ".pyw", ".txt")
def get_lib_layout(ns):
def _c(f):
if f in EXCLUDE_FROM_LIB:
return False
if f.is_dir():
if f in TEST_DIRS_ONLY:
return ns.include_tests
if f in TCLTK_DIRS_ONLY:
return ns.include_tcltk
if f in IDLE_DIRS_ONLY:
return ns.include_idle
if f in VENV_DIRS_ONLY:
return ns.include_venv
else:
if f in TCLTK_FILES_ONLY:
return ns.include_tcltk
if f in BDIST_WININST_FILES_ONLY:
return ns.include_bdist_wininst
return True
for dest, src in rglob(ns.source / "Lib", "**/*", _c):
yield dest, src
if not ns.include_bdist_wininst:
src = ns.source / BDIST_WININST_STUB
yield Path("distutils/command/bdist_wininst.py"), src
def get_tcltk_lib(ns):
if not ns.include_tcltk:
return
tcl_lib = os.getenv("TCL_LIBRARY")
if not tcl_lib or not os.path.isdir(tcl_lib):
try:
with open(ns.build / "TCL_LIBRARY.env", "r", encoding="utf-8-sig") as f:
tcl_lib = f.read().strip()
except FileNotFoundError:
pass
if not tcl_lib or not os.path.isdir(tcl_lib):
warn("Failed to find TCL_LIBRARY")
return
for dest, src in rglob(Path(tcl_lib).parent, "**/*"):
yield "tcl/{}".format(dest), src
def get_layout(ns):
def in_build(f, dest="", new_name=None):
n, _, x = f.rpartition(".")
n = new_name or n
src = ns.build / f
if ns.debug and src not in REQUIRED_DLLS:
if not src.stem.endswith("_d"):
src = src.parent / (src.stem + "_d" + src.suffix)
if not n.endswith("_d"):
n += "_d"
f = n + "." + x
yield dest + n + "." + x, src
if ns.include_symbols:
pdb = src.with_suffix(".pdb")
if pdb.is_file():
yield dest + n + ".pdb", pdb
if ns.include_dev:
lib = src.with_suffix(".lib")
if lib.is_file():
yield "libs/" + n + ".lib", lib
yield from in_build("python_uwp.exe", new_name="python")
yield from in_build("pythonw_uwp.exe", new_name="pythonw")
yield from in_build(PYTHON_DLL_NAME)
if ns.include_launchers:
if ns.include_pip:
yield from in_build("python_uwp.exe", new_name="pip")
if ns.include_idle:
yield from in_build("pythonw_uwp.exe", new_name="idle")
if ns.include_stable:
yield from in_build(PYTHON_STABLE_DLL_NAME)
for dest, src in rglob(ns.build, "vcruntime*.dll"):
yield dest, src
for dest, src in rglob(ns.build, ("*.pyd", "*.dll")):
if src.stem.endswith("_d") != bool(ns.debug) and src not in REQUIRED_DLLS:
continue
if src in EXCLUDE_FROM_PYDS:
continue
if src in TEST_PYDS_ONLY and not ns.include_tests:
continue
if src in TCLTK_PYDS_ONLY and not ns.include_tcltk:
continue
yield from in_build(src.name, dest="" if ns.flat_dlls else "DLLs/")
if ns.zip_lib:
zip_name = PYTHON_ZIP_NAME
yield zip_name, ns.temp / zip_name
else:
for dest, src in get_lib_layout(ns):
yield "Lib/{}".format(dest), src
if ns.include_venv:
yield from in_build("venvlauncher.exe", "Lib/venv/scripts/nt/", "python")
yield from in_build("venvwlauncher.exe", "Lib/venv/scripts/nt/", "pythonw")
if ns.include_tools:
def _c(d):
if d.is_dir():
return d in TOOLS_DIRS
return d in TOOLS_FILES
for dest, src in rglob(ns.source / "Tools", "**/*", _c):
yield "Tools/{}".format(dest), src
if ns.include_underpth:
yield PYTHON_PTH_NAME, ns.temp / PYTHON_PTH_NAME
if ns.include_dev:
def _c(d):
if d.is_dir():
return d.name != "internal"
return True
for dest, src in rglob(ns.source / "Include", "**/*.h", _c):
yield "include/{}".format(dest), src
src = ns.source / "PC" / "pyconfig.h"
yield "include/pyconfig.h", src
for dest, src in get_tcltk_lib(ns):
yield dest, src
if ns.include_pip:
pip_dir = get_pip_dir(ns)
if not pip_dir.is_dir():
log_warning("Failed to find {} - pip will not be included", pip_dir)
else:
pkg_root = "packages/{}" if ns.zip_lib else "Lib/site-packages/{}"
for dest, src in rglob(pip_dir, "**/*"):
if src in EXCLUDE_FROM_LIB or src in EXCLUDE_FROM_PACKAGED_LIB:
continue
yield pkg_root.format(dest), src
if ns.include_chm:
for dest, src in rglob(ns.doc_build / "htmlhelp", PYTHON_CHM_NAME):
yield "Doc/{}".format(dest), src
if ns.include_html_doc:
for dest, src in rglob(ns.doc_build / "html", "**/*"):
yield "Doc/html/{}".format(dest), src
if ns.include_props:
for dest, src in get_props_layout(ns):
yield dest, src
for dest, src in get_appx_layout(ns):
yield dest, src
if ns.include_cat:
if ns.flat_dlls:
yield ns.include_cat.name, ns.include_cat
else:
yield "DLLs/{}".format(ns.include_cat.name), ns.include_cat
def _compile_one_py(src, dest, name, optimize):
import py_compile
if dest is not None:
dest = str(dest)
try:
return Path(
py_compile.compile(
str(src),
dest,
str(name),
doraise=True,
optimize=optimize,
invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH,
)
)
except py_compile.PyCompileError:
log_warning("Failed to compile {}", src)
return None
def _py_temp_compile(src, ns, dest_dir=None):
if not ns.precompile or src not in PY_FILES or src.parent in DATA_DIRS:
return None
dest = (dest_dir or ns.temp) / (src.stem + ".py")
return _compile_one_py(src, dest.with_suffix(".pyc"), dest, optimize=2)
def _write_to_zip(zf, dest, src, ns):
pyc = _py_temp_compile(src, ns)
if pyc:
try:
zf.write(str(pyc), dest.with_suffix(".pyc"))
finally:
try:
pyc.unlink()
except:
log_exception("Failed to delete {}", pyc)
return
if src in LIB2TO3_GRAMMAR_FILES:
from lib2to3.pgen2.driver import load_grammar
tmp = ns.temp / src.name
try:
shutil.copy(src, tmp)
load_grammar(str(tmp))
for f in ns.temp.glob(src.stem + "*.pickle"):
zf.write(str(f), str(dest.parent / f.name))
try:
f.unlink()
except:
log_exception("Failed to delete {}", f)
except:
log_exception("Failed to compile {}", src)
finally:
try:
tmp.unlink()
except:
log_exception("Failed to delete {}", tmp)
zf.write(str(src), str(dest))
def generate_source_files(ns):
if ns.zip_lib:
zip_name = PYTHON_ZIP_NAME
zip_path = ns.temp / zip_name
if zip_path.is_file():
zip_path.unlink()
elif zip_path.is_dir():
log_error(
"Cannot create zip file because a directory exists by the same name"
)
return
log_info("Generating {} in {}", zip_name, ns.temp)
ns.temp.mkdir(parents=True, exist_ok=True)
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
for dest, src in get_lib_layout(ns):
_write_to_zip(zf, dest, src, ns)
if ns.include_underpth:
log_info("Generating {} in {}", PYTHON_PTH_NAME, ns.temp)
ns.temp.mkdir(parents=True, exist_ok=True)
with open(ns.temp / PYTHON_PTH_NAME, "w", encoding="utf-8") as f:
if ns.zip_lib:
print(PYTHON_ZIP_NAME, file=f)
if ns.include_pip:
print("packages", file=f)
else:
print("Lib", file=f)
print("Lib/site-packages", file=f)
if not ns.flat_dlls:
print("DLLs", file=f)
print(".", file=f)
print(file=f)
print("# Uncomment to run site.main() automatically", file=f)
print("#import site", file=f)
if ns.include_appxmanifest:
log_info("Generating AppxManifest.xml in {}", ns.temp)
ns.temp.mkdir(parents=True, exist_ok=True)
with open(ns.temp / "AppxManifest.xml", "wb") as f:
f.write(get_appxmanifest(ns))
with open(ns.temp / "_resources.xml", "wb") as f:
f.write(get_resources_xml(ns))
if ns.include_pip:
pip_dir = get_pip_dir(ns)
if not (pip_dir / "pip").is_dir():
log_info("Extracting pip to {}", pip_dir)
pip_dir.mkdir(parents=True, exist_ok=True)
extract_pip_files(ns)
if ns.include_props:
log_info("Generating {} in {}", PYTHON_PROPS_NAME, ns.temp)
ns.temp.mkdir(parents=True, exist_ok=True)
with open(ns.temp / PYTHON_PROPS_NAME, "wb") as f:
f.write(get_props(ns))
def _create_zip_file(ns):
if not ns.zip:
return None
if ns.zip.is_file():
try:
ns.zip.unlink()
except OSError:
log_exception("Unable to remove {}", ns.zip)
sys.exit(8)
elif ns.zip.is_dir():
log_error("Cannot create ZIP file because {} is a directory", ns.zip)
sys.exit(8)
ns.zip.parent.mkdir(parents=True, exist_ok=True)
return zipfile.ZipFile(ns.zip, "w", zipfile.ZIP_DEFLATED)
def copy_files(files, ns):
if ns.copy:
ns.copy.mkdir(parents=True, exist_ok=True)
try:
total = len(files)
except TypeError:
total = None
count = 0
zip_file = _create_zip_file(ns)
try:
need_compile = []
in_catalog = []
for dest, src in files:
count += 1
if count % 10 == 0:
if total:
log_info("Processed {:>4} of {} files", count, total)
else:
log_info("Processed {} files", count)
log_debug("Processing {!s}", src)
if (
ns.precompile
and src in PY_FILES
and src not in EXCLUDE_FROM_COMPILE
and src.parent not in DATA_DIRS
and os.path.normcase(str(dest)).startswith(os.path.normcase("Lib"))
):
if ns.copy:
need_compile.append((dest, ns.copy / dest))
else:
(ns.temp / "Lib" / dest).parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src, ns.temp / "Lib" / dest)
need_compile.append((dest, ns.temp / "Lib" / dest))
if src not in EXCLUDE_FROM_CATALOG:
in_catalog.append((src.name, src))
if ns.copy:
log_debug("Copy {} -> {}", src, ns.copy / dest)
(ns.copy / dest).parent.mkdir(parents=True, exist_ok=True)
try:
shutil.copy2(src, ns.copy / dest)
except shutil.SameFileError:
pass
if ns.zip:
log_debug("Zip {} into {}", src, ns.zip)
zip_file.write(src, str(dest))
if need_compile:
for dest, src in need_compile:
compiled = [
_compile_one_py(src, None, dest, optimize=0),
_compile_one_py(src, None, dest, optimize=1),
_compile_one_py(src, None, dest, optimize=2),
]
for c in compiled:
if not c:
continue
cdest = Path(dest).parent / Path(c).relative_to(src.parent)
if ns.zip:
log_debug("Zip {} into {}", c, ns.zip)
zip_file.write(c, str(cdest))
in_catalog.append((cdest.name, cdest))
if ns.catalog:
# Just write out the CDF now. Compilation and signing is
# an extra step
log_info("Generating {}", ns.catalog)
ns.catalog.parent.mkdir(parents=True, exist_ok=True)
write_catalog(ns.catalog, in_catalog)
finally:
if zip_file:
zip_file.close()
def main():
parser = argparse.ArgumentParser()
parser.add_argument("-v", help="Increase verbosity", action="count")
parser.add_argument(
"-s",
"--source",
metavar="dir",
help="The directory containing the repository root",
type=Path,
default=None,
)
parser.add_argument(
"-b", "--build", metavar="dir", help="Specify the build directory", type=Path
)
parser.add_argument(
"--doc-build",
metavar="dir",
help="Specify the docs build directory",
type=Path,
default=None,
)
parser.add_argument(
"--copy",
metavar="directory",
help="The name of the directory to copy an extracted layout to",
type=Path,
default=None,
)
parser.add_argument(
"--zip",
metavar="file",
help="The ZIP file to write all files to",
type=Path,
default=None,
)
parser.add_argument(
"--catalog",
metavar="file",
help="The CDF file to write catalog entries to",
type=Path,
default=None,
)
parser.add_argument(
"--log",
metavar="file",
help="Write all operations to the specified file",
type=Path,
default=None,
)
parser.add_argument(
"-t",
"--temp",
metavar="file",
help="A temporary working directory",
type=Path,
default=None,
)
parser.add_argument(
"-d", "--debug", help="Include debug build", action="store_true"
)
parser.add_argument(
"-p",
"--precompile",
help="Include .pyc files instead of .py",
action="store_true",
)
parser.add_argument(
"-z", "--zip-lib", help="Include library in a ZIP file", action="store_true"
)
parser.add_argument(
"--flat-dlls", help="Does not create a DLLs directory", action="store_true"
)
parser.add_argument(
"-a",
"--include-all",
help="Include all optional components",
action="store_true",
)
parser.add_argument(
"--include-cat",
metavar="file",
help="Specify the catalog file to include",
type=Path,
default=None,
)
for opt, help in get_argparse_options():
parser.add_argument(opt, help=help, action="store_true")
ns = parser.parse_args()
update_presets(ns)
ns.source = ns.source or (Path(__file__).resolve().parent.parent.parent)
ns.build = ns.build or Path(sys.executable).parent
ns.temp = ns.temp or Path(tempfile.mkdtemp())
ns.doc_build = ns.doc_build or (ns.source / "Doc" / "build")
if not ns.source.is_absolute():
ns.source = (Path.cwd() / ns.source).resolve()
if not ns.build.is_absolute():
ns.build = (Path.cwd() / ns.build).resolve()
if not ns.temp.is_absolute():
ns.temp = (Path.cwd() / ns.temp).resolve()
if not ns.doc_build.is_absolute():
ns.doc_build = (Path.cwd() / ns.doc_build).resolve()
if ns.include_cat and not ns.include_cat.is_absolute():
ns.include_cat = (Path.cwd() / ns.include_cat).resolve()
if ns.copy and not ns.copy.is_absolute():
ns.copy = (Path.cwd() / ns.copy).resolve()
if ns.zip and not ns.zip.is_absolute():
ns.zip = (Path.cwd() / ns.zip).resolve()
if ns.catalog and not ns.catalog.is_absolute():
ns.catalog = (Path.cwd() / ns.catalog).resolve()
configure_logger(ns)
log_info(
"""OPTIONS
Source: {ns.source}
Build: {ns.build}
Temp: {ns.temp}
Copy to: {ns.copy}
Zip to: {ns.zip}
Catalog: {ns.catalog}""",
ns=ns,
)
if ns.include_idle and not ns.include_tcltk:
log_warning("Assuming --include-tcltk to support --include-idle")
ns.include_tcltk = True
try:
generate_source_files(ns)
files = list(get_layout(ns))
copy_files(files, ns)
except KeyboardInterrupt:
log_info("Interrupted by Ctrl+C")
return 3
except SystemExit:
raise
except:
log_exception("Unhandled error")
if error_was_logged():
log_error("Errors occurred.")
return 1
if __name__ == "__main__":
sys.exit(int(main() or 0))

View File

View File

@ -0,0 +1,487 @@
"""
File generation for APPX/MSIX manifests.
"""
__author__ = "Steve Dower <steve.dower@python.org>"
__version__ = "3.8"
import collections
import ctypes
import io
import os
import sys
from pathlib import Path, PureWindowsPath
from xml.etree import ElementTree as ET
from .constants import *
__all__ = []
def public(f):
__all__.append(f.__name__)
return f
APPX_DATA = dict(
Name="PythonSoftwareFoundation.Python.{}".format(VER_DOT),
Version="{}.{}.{}.0".format(VER_MAJOR, VER_MINOR, VER_FIELD3),
Publisher=os.getenv(
"APPX_DATA_PUBLISHER", "CN=4975D53F-AA7E-49A5-8B49-EA4FDC1BB66B"
),
DisplayName="Python {}".format(VER_DOT),
Description="The Python {} runtime and console.".format(VER_DOT),
ProcessorArchitecture="x64" if IS_X64 else "x86",
)
PYTHON_VE_DATA = dict(
DisplayName="Python {}".format(VER_DOT),
Description="Python interactive console",
Square150x150Logo="_resources/pythonx150.png",
Square44x44Logo="_resources/pythonx44.png",
BackgroundColor="transparent",
)
PYTHONW_VE_DATA = dict(
DisplayName="Python {} (Windowed)".format(VER_DOT),
Description="Python windowed app launcher",
Square150x150Logo="_resources/pythonwx150.png",
Square44x44Logo="_resources/pythonwx44.png",
BackgroundColor="transparent",
AppListEntry="none",
)
PIP_VE_DATA = dict(
DisplayName="pip (Python {})".format(VER_DOT),
Description="pip package manager for Python {}".format(VER_DOT),
Square150x150Logo="_resources/pythonx150.png",
Square44x44Logo="_resources/pythonx44.png",
BackgroundColor="transparent",
AppListEntry="none",
)
IDLE_VE_DATA = dict(
DisplayName="IDLE (Python {})".format(VER_DOT),
Description="IDLE editor for Python {}".format(VER_DOT),
Square150x150Logo="_resources/pythonwx150.png",
Square44x44Logo="_resources/pythonwx44.png",
BackgroundColor="transparent",
)
APPXMANIFEST_NS = {
"": "http://schemas.microsoft.com/appx/manifest/foundation/windows10",
"m": "http://schemas.microsoft.com/appx/manifest/foundation/windows10",
"uap": "http://schemas.microsoft.com/appx/manifest/uap/windows10",
"rescap": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities",
"rescap4": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/4",
"desktop4": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/4",
"desktop6": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/6",
"uap3": "http://schemas.microsoft.com/appx/manifest/uap/windows10/3",
"uap4": "http://schemas.microsoft.com/appx/manifest/uap/windows10/4",
"uap5": "http://schemas.microsoft.com/appx/manifest/uap/windows10/5",
}
APPXMANIFEST_TEMPLATE = """<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns:rescap4="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/4"
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4"
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5">
<Identity Name=""
Version=""
Publisher=""
ProcessorArchitecture="" />
<Properties>
<DisplayName></DisplayName>
<PublisherDisplayName>Python Software Foundation</PublisherDisplayName>
<Description></Description>
<Logo>_resources/pythonx50.png</Logo>
</Properties>
<Resources>
<Resource Language="en-US" />
</Resources>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="" />
</Dependencies>
<Capabilities>
<rescap:Capability Name="runFullTrust"/>
</Capabilities>
<Applications>
</Applications>
<Extensions>
</Extensions>
</Package>"""
RESOURCES_XML_TEMPLATE = r"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--This file is input for makepri.exe. It should be excluded from the final package.-->
<resources targetOsVersion="10.0.0" majorVersion="1">
<packaging>
<autoResourcePackage qualifier="Language"/>
<autoResourcePackage qualifier="Scale"/>
<autoResourcePackage qualifier="DXFeatureLevel"/>
</packaging>
<index root="\" startIndexAt="\">
<default>
<qualifier name="Language" value="en-US"/>
<qualifier name="Contrast" value="standard"/>
<qualifier name="Scale" value="100"/>
<qualifier name="HomeRegion" value="001"/>
<qualifier name="TargetSize" value="256"/>
<qualifier name="LayoutDirection" value="LTR"/>
<qualifier name="Theme" value="dark"/>
<qualifier name="AlternateForm" value=""/>
<qualifier name="DXFeatureLevel" value="DX9"/>
<qualifier name="Configuration" value=""/>
<qualifier name="DeviceFamily" value="Universal"/>
<qualifier name="Custom" value=""/>
</default>
<indexer-config type="folder" foldernameAsQualifier="true" filenameAsQualifier="true" qualifierDelimiter="$"/>
<indexer-config type="resw" convertDotsToSlashes="true" initialPath=""/>
<indexer-config type="resjson" initialPath=""/>
<indexer-config type="PRI"/>
</index>
</resources>"""
SCCD_FILENAME = "PC/classicAppCompat.sccd"
REGISTRY = {
"HKCU\\Software\\Python\\PythonCore": {
VER_DOT: {
"DisplayName": APPX_DATA["DisplayName"],
"SupportUrl": "https://www.python.org/",
"SysArchitecture": "64bit" if IS_X64 else "32bit",
"SysVersion": VER_DOT,
"Version": "{}.{}.{}".format(VER_MAJOR, VER_MINOR, VER_MICRO),
"InstallPath": {
# I have no idea why the trailing spaces are needed, but they seem to be needed.
"": "[{AppVPackageRoot}][ ]",
"ExecutablePath": "[{AppVPackageRoot}]python.exe[ ]",
"WindowedExecutablePath": "[{AppVPackageRoot}]pythonw.exe[ ]",
},
"Help": {
"Main Python Documentation": {
"_condition": lambda ns: ns.include_chm,
"": "[{{AppVPackageRoot}}]Doc\\{}[ ]".format(
PYTHON_CHM_NAME
),
},
"Local Python Documentation": {
"_condition": lambda ns: ns.include_html_doc,
"": "[{AppVPackageRoot}]Doc\\html\\index.html[ ]",
},
"Online Python Documentation": {
"": "https://docs.python.org/{}".format(VER_DOT)
},
},
"Idle": {
"_condition": lambda ns: ns.include_idle,
"": "[{AppVPackageRoot}]Lib\\idlelib\\idle.pyw[ ]",
},
}
}
}
def get_packagefamilyname(name, publisher_id):
class PACKAGE_ID(ctypes.Structure):
_fields_ = [
("reserved", ctypes.c_uint32),
("processorArchitecture", ctypes.c_uint32),
("version", ctypes.c_uint64),
("name", ctypes.c_wchar_p),
("publisher", ctypes.c_wchar_p),
("resourceId", ctypes.c_wchar_p),
("publisherId", ctypes.c_wchar_p),
]
_pack_ = 4
pid = PACKAGE_ID(0, 0, 0, name, publisher_id, None, None)
result = ctypes.create_unicode_buffer(256)
result_len = ctypes.c_uint32(256)
r = ctypes.windll.kernel32.PackageFamilyNameFromId(
pid, ctypes.byref(result_len), result
)
if r:
raise OSError(r, "failed to get package family name")
return result.value[: result_len.value]
def _fixup_sccd(ns, sccd, new_hash=None):
if not new_hash:
return sccd
NS = dict(s="http://schemas.microsoft.com/appx/2016/sccd")
with open(sccd, "rb") as f:
xml = ET.parse(f)
pfn = get_packagefamilyname(APPX_DATA["Name"], APPX_DATA["Publisher"])
ae = xml.find("s:AuthorizedEntities", NS)
ae.clear()
e = ET.SubElement(ae, ET.QName(NS["s"], "AuthorizedEntity"))
e.set("AppPackageFamilyName", pfn)
e.set("CertificateSignatureHash", new_hash)
for e in xml.findall("s:Catalog", NS):
e.text = "FFFF"
sccd = ns.temp / sccd.name
sccd.parent.mkdir(parents=True, exist_ok=True)
with open(sccd, "wb") as f:
xml.write(f, encoding="utf-8")
return sccd
@public
def get_appx_layout(ns):
if not ns.include_appxmanifest:
return
yield "AppxManifest.xml", ns.temp / "AppxManifest.xml"
yield "_resources.xml", ns.temp / "_resources.xml"
icons = ns.source / "PC" / "icons"
yield "_resources/pythonx44.png", icons / "pythonx44.png"
yield "_resources/pythonx44$targetsize-44_altform-unplated.png", icons / "pythonx44.png"
yield "_resources/pythonx50.png", icons / "pythonx50.png"
yield "_resources/pythonx50$targetsize-50_altform-unplated.png", icons / "pythonx50.png"
yield "_resources/pythonx150.png", icons / "pythonx150.png"
yield "_resources/pythonx150$targetsize-150_altform-unplated.png", icons / "pythonx150.png"
yield "_resources/pythonwx44.png", icons / "pythonwx44.png"
yield "_resources/pythonwx44$targetsize-44_altform-unplated.png", icons / "pythonwx44.png"
yield "_resources/pythonwx150.png", icons / "pythonwx150.png"
yield "_resources/pythonwx150$targetsize-150_altform-unplated.png", icons / "pythonwx150.png"
sccd = ns.source / SCCD_FILENAME
if sccd.is_file():
# This should only be set for side-loading purposes.
sccd = _fixup_sccd(ns, sccd, os.getenv("APPX_DATA_SHA256"))
yield sccd.name, sccd
def find_or_add(xml, element, attr=None, always_add=False):
if always_add:
e = None
else:
q = element
if attr:
q += "[@{}='{}']".format(*attr)
e = xml.find(q, APPXMANIFEST_NS)
if e is None:
prefix, _, name = element.partition(":")
name = ET.QName(APPXMANIFEST_NS[prefix or ""], name)
e = ET.SubElement(xml, name)
if attr:
e.set(*attr)
return e
def _get_app(xml, appid):
if appid:
app = xml.find(
"m:Applications/m:Application[@Id='{}']".format(appid), APPXMANIFEST_NS
)
if app is None:
raise LookupError(appid)
else:
app = xml
return app
def add_visual(xml, appid, data):
app = _get_app(xml, appid)
e = find_or_add(app, "uap:VisualElements")
for i in data.items():
e.set(*i)
return e
def add_alias(xml, appid, alias, subsystem="windows"):
app = _get_app(xml, appid)
e = find_or_add(app, "m:Extensions")
e = find_or_add(e, "uap5:Extension", ("Category", "windows.appExecutionAlias"))
e = find_or_add(e, "uap5:AppExecutionAlias")
e.set(ET.QName(APPXMANIFEST_NS["desktop4"], "Subsystem"), subsystem)
e = find_or_add(e, "uap5:ExecutionAlias", ("Alias", alias))
def add_file_type(xml, appid, name, suffix, parameters='"%1"'):
app = _get_app(xml, appid)
e = find_or_add(app, "m:Extensions")
e = find_or_add(e, "uap3:Extension", ("Category", "windows.fileTypeAssociation"))
e = find_or_add(e, "uap3:FileTypeAssociation", ("Name", name))
e.set("Parameters", parameters)
e = find_or_add(e, "uap:SupportedFileTypes")
if isinstance(suffix, str):
suffix = [suffix]
for s in suffix:
ET.SubElement(e, ET.QName(APPXMANIFEST_NS["uap"], "FileType")).text = s
def add_application(
ns, xml, appid, executable, aliases, visual_element, subsystem, file_types
):
node = xml.find("m:Applications", APPXMANIFEST_NS)
suffix = "_d.exe" if ns.debug else ".exe"
app = ET.SubElement(
node,
ET.QName(APPXMANIFEST_NS[""], "Application"),
{
"Id": appid,
"Executable": executable + suffix,
"EntryPoint": "Windows.FullTrustApplication",
ET.QName(APPXMANIFEST_NS["desktop4"], "SupportsMultipleInstances"): "true",
},
)
if visual_element:
add_visual(app, None, visual_element)
for alias in aliases:
add_alias(app, None, alias + suffix, subsystem)
if file_types:
add_file_type(app, None, *file_types)
return app
def _get_registry_entries(ns, root="", d=None):
r = root if root else PureWindowsPath("")
if d is None:
d = REGISTRY
for key, value in d.items():
if key == "_condition":
continue
elif isinstance(value, dict):
cond = value.get("_condition")
if cond and not cond(ns):
continue
fullkey = r
for part in PureWindowsPath(key).parts:
fullkey /= part
if len(fullkey.parts) > 1:
yield str(fullkey), None, None
yield from _get_registry_entries(ns, fullkey, value)
elif len(r.parts) > 1:
yield str(r), key, value
def add_registry_entries(ns, xml):
e = find_or_add(xml, "m:Extensions")
e = find_or_add(e, "rescap4:Extension")
e.set("Category", "windows.classicAppCompatKeys")
e.set("EntryPoint", "Windows.FullTrustApplication")
e = ET.SubElement(e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKeys"))
for name, valuename, value in _get_registry_entries(ns):
k = ET.SubElement(
e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKey")
)
k.set("Name", name)
if value:
k.set("ValueName", valuename)
k.set("Value", value)
k.set("ValueType", "REG_SZ")
def disable_registry_virtualization(xml):
e = find_or_add(xml, "m:Properties")
e = find_or_add(e, "desktop6:RegistryWriteVirtualization")
e.text = "disabled"
e = find_or_add(xml, "m:Capabilities")
e = find_or_add(e, "rescap:Capability", ("Name", "unvirtualizedResources"))
@public
def get_appxmanifest(ns):
for k, v in APPXMANIFEST_NS.items():
ET.register_namespace(k, v)
ET.register_namespace("", APPXMANIFEST_NS["m"])
xml = ET.parse(io.StringIO(APPXMANIFEST_TEMPLATE))
NS = APPXMANIFEST_NS
QN = ET.QName
node = xml.find("m:Identity", NS)
for k in node.keys():
value = APPX_DATA.get(k)
if value:
node.set(k, value)
for node in xml.find("m:Properties", NS):
value = APPX_DATA.get(node.tag.rpartition("}")[2])
if value:
node.text = value
winver = sys.getwindowsversion()[:3]
if winver < (10, 0, 17763):
winver = 10, 0, 17763
find_or_add(xml, "m:Dependencies/m:TargetDeviceFamily").set(
"MaxVersionTested", "{}.{}.{}.0".format(*winver)
)
if winver > (10, 0, 17763):
disable_registry_virtualization(xml)
app = add_application(
ns,
xml,
"Python",
"python",
["python", "python{}".format(VER_MAJOR), "python{}".format(VER_DOT)],
PYTHON_VE_DATA,
"console",
("python.file", [".py"]),
)
add_application(
ns,
xml,
"PythonW",
"pythonw",
["pythonw", "pythonw{}".format(VER_MAJOR), "pythonw{}".format(VER_DOT)],
PYTHONW_VE_DATA,
"windows",
("python.windowedfile", [".pyw"]),
)
if ns.include_pip and ns.include_launchers:
add_application(
ns,
xml,
"Pip",
"pip",
["pip", "pip{}".format(VER_MAJOR), "pip{}".format(VER_DOT)],
PIP_VE_DATA,
"console",
("python.wheel", [".whl"], 'install "%1"'),
)
if ns.include_idle and ns.include_launchers:
add_application(
ns,
xml,
"Idle",
"idle",
["idle", "idle{}".format(VER_MAJOR), "idle{}".format(VER_DOT)],
IDLE_VE_DATA,
"windows",
None,
)
if (ns.source / SCCD_FILENAME).is_file():
add_registry_entries(ns, xml)
node = xml.find("m:Capabilities", NS)
node = ET.SubElement(node, QN(NS["uap4"], "CustomCapability"))
node.set("Name", "Microsoft.classicAppCompat_8wekyb3d8bbwe")
buffer = io.BytesIO()
xml.write(buffer, encoding="utf-8", xml_declaration=True)
return buffer.getbuffer()
@public
def get_resources_xml(ns):
return RESOURCES_XML_TEMPLATE.encode("utf-8")

View File

@ -0,0 +1,44 @@
"""
File generation for catalog signing non-binary contents.
"""
__author__ = "Steve Dower <steve.dower@python.org>"
__version__ = "3.8"
import sys
__all__ = ["PYTHON_CAT_NAME", "PYTHON_CDF_NAME"]
def public(f):
__all__.append(f.__name__)
return f
PYTHON_CAT_NAME = "python.cat"
PYTHON_CDF_NAME = "python.cdf"
CATALOG_TEMPLATE = r"""[CatalogHeader]
Name={target.stem}.cat
ResultDir={target.parent}
PublicVersion=1
CatalogVersion=2
HashAlgorithms=SHA256
PageHashes=false
EncodingType=
[CatalogFiles]
"""
def can_sign(file):
return file.is_file() and file.stat().st_size
@public
def write_catalog(target, files):
with target.open("w", encoding="utf-8") as cat:
cat.write(CATALOG_TEMPLATE.format(target=target))
cat.writelines("<HASH>{}={}\n".format(n, f) for n, f in files if can_sign(f))

View File

@ -0,0 +1,28 @@
"""
Constants for generating the layout.
"""
__author__ = "Steve Dower <steve.dower@python.org>"
__version__ = "3.8"
import struct
import sys
VER_MAJOR, VER_MINOR, VER_MICRO, VER_FIELD4 = struct.pack(">i", sys.hexversion)
VER_FIELD3 = VER_MICRO << 8 | VER_FIELD4
VER_NAME = {"alpha": "a", "beta": "b", "rc": "rc"}.get(
sys.version_info.releaselevel, ""
)
VER_SERIAL = sys.version_info.serial if VER_NAME else ""
VER_DOT = "{}.{}".format(VER_MAJOR, VER_MINOR)
PYTHON_DLL_NAME = "python{}{}.dll".format(VER_MAJOR, VER_MINOR)
PYTHON_STABLE_DLL_NAME = "python{}.dll".format(VER_MAJOR)
PYTHON_ZIP_NAME = "python{}{}.zip".format(VER_MAJOR, VER_MINOR)
PYTHON_PTH_NAME = "python{}{}._pth".format(VER_MAJOR, VER_MINOR)
PYTHON_CHM_NAME = "python{}{}{}{}{}.chm".format(
VER_MAJOR, VER_MINOR, VER_MICRO, VER_NAME, VER_SERIAL
)
IS_X64 = sys.maxsize > 2 ** 32

View File

@ -0,0 +1,25 @@
"""distutils.command.bdist_wininst
Suppress the 'bdist_wininst' command, while still allowing
setuptools to import it without breaking."""
from distutils.core import Command
from distutils.errors import DistutilsPlatformError
class bdist_wininst(Command):
description = "create an executable installer for MS Windows"
# Marker for tests that we have the unsupported bdist_wininst
_unsupported = True
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
raise DistutilsPlatformError(
"bdist_wininst is not supported in this Python distribution"
)

View File

@ -0,0 +1,100 @@
"""
File sets and globbing helper for make_layout.
"""
__author__ = "Steve Dower <steve.dower@python.org>"
__version__ = "3.8"
import os
class FileStemSet:
def __init__(self, *patterns):
self._names = set()
self._prefixes = []
self._suffixes = []
for p in map(os.path.normcase, patterns):
if p.endswith("*"):
self._prefixes.append(p[:-1])
elif p.startswith("*"):
self._suffixes.append(p[1:])
else:
self._names.add(p)
def _make_name(self, f):
return os.path.normcase(f.stem)
def __contains__(self, f):
bn = self._make_name(f)
return (
bn in self._names
or any(map(bn.startswith, self._prefixes))
or any(map(bn.endswith, self._suffixes))
)
class FileNameSet(FileStemSet):
def _make_name(self, f):
return os.path.normcase(f.name)
class FileSuffixSet:
def __init__(self, *patterns):
self._names = set()
self._prefixes = []
self._suffixes = []
for p in map(os.path.normcase, patterns):
if p.startswith("*."):
self._names.add(p[1:])
elif p.startswith("*"):
self._suffixes.append(p[1:])
elif p.endswith("*"):
self._prefixes.append(p[:-1])
elif p.startswith("."):
self._names.add(p)
else:
self._names.add("." + p)
def _make_name(self, f):
return os.path.normcase(f.suffix)
def __contains__(self, f):
bn = self._make_name(f)
return (
bn in self._names
or any(map(bn.startswith, self._prefixes))
or any(map(bn.endswith, self._suffixes))
)
def _rglob(root, pattern, condition):
dirs = [root]
recurse = pattern[:3] in {"**/", "**\\"}
if recurse:
pattern = pattern[3:]
while dirs:
d = dirs.pop(0)
if recurse:
dirs.extend(
filter(
condition, (type(root)(f2) for f2 in os.scandir(d) if f2.is_dir())
)
)
yield from (
(f.relative_to(root), f)
for f in d.glob(pattern)
if f.is_file() and condition(f)
)
def _return_true(f):
return True
def rglob(root, patterns, condition=None):
if isinstance(patterns, tuple):
for p in patterns:
yield from _rglob(root, p, condition or _return_true)
else:
yield from _rglob(root, patterns, condition or _return_true)

View File

@ -0,0 +1,93 @@
"""
Logging support for make_layout.
"""
__author__ = "Steve Dower <steve.dower@python.org>"
__version__ = "3.8"
import logging
import sys
__all__ = []
LOG = None
HAS_ERROR = False
def public(f):
__all__.append(f.__name__)
return f
@public
def configure_logger(ns):
global LOG
if LOG:
return
LOG = logging.getLogger("make_layout")
LOG.level = logging.DEBUG
if ns.v:
s_level = max(logging.ERROR - ns.v * 10, logging.DEBUG)
f_level = max(logging.WARNING - ns.v * 10, logging.DEBUG)
else:
s_level = logging.ERROR
f_level = logging.INFO
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(logging.Formatter("{levelname:8s} {message}", style="{"))
handler.setLevel(s_level)
LOG.addHandler(handler)
if ns.log:
handler = logging.FileHandler(ns.log, encoding="utf-8", delay=True)
handler.setFormatter(
logging.Formatter("[{asctime}]{levelname:8s}: {message}", style="{")
)
handler.setLevel(f_level)
LOG.addHandler(handler)
class BraceMessage:
def __init__(self, fmt, *args, **kwargs):
self.fmt = fmt
self.args = args
self.kwargs = kwargs
def __str__(self):
return self.fmt.format(*self.args, **self.kwargs)
@public
def log_debug(msg, *args, **kwargs):
return LOG.debug(BraceMessage(msg, *args, **kwargs))
@public
def log_info(msg, *args, **kwargs):
return LOG.info(BraceMessage(msg, *args, **kwargs))
@public
def log_warning(msg, *args, **kwargs):
return LOG.warning(BraceMessage(msg, *args, **kwargs))
@public
def log_error(msg, *args, **kwargs):
global HAS_ERROR
HAS_ERROR = True
return LOG.error(BraceMessage(msg, *args, **kwargs))
@public
def log_exception(msg, *args, **kwargs):
global HAS_ERROR
HAS_ERROR = True
return LOG.exception(BraceMessage(msg, *args, **kwargs))
@public
def error_was_logged():
return HAS_ERROR

View File

@ -0,0 +1,122 @@
"""
List of optional components.
"""
__author__ = "Steve Dower <steve.dower@python.org>"
__version__ = "3.8"
__all__ = []
def public(f):
__all__.append(f.__name__)
return f
OPTIONS = {
"stable": {"help": "stable ABI stub"},
"pip": {"help": "pip"},
"distutils": {"help": "distutils"},
"tcltk": {"help": "Tcl, Tk and tkinter"},
"idle": {"help": "Idle"},
"tests": {"help": "test suite"},
"tools": {"help": "tools"},
"venv": {"help": "venv"},
"dev": {"help": "headers and libs"},
"symbols": {"help": "symbols"},
"bdist-wininst": {"help": "bdist_wininst support"},
"underpth": {"help": "a python._pth file", "not-in-all": True},
"launchers": {"help": "specific launchers"},
"appxmanifest": {"help": "an appxmanifest"},
"props": {"help": "a python.props file"},
"chm": {"help": "the CHM documentation"},
"html-doc": {"help": "the HTML documentation"},
}
PRESETS = {
"appx": {
"help": "APPX package",
"options": [
"stable",
"pip",
"distutils",
"tcltk",
"idle",
"venv",
"dev",
"launchers",
"appxmanifest",
# XXX: Disabled for now "precompile",
],
},
"nuget": {
"help": "nuget package",
"options": ["stable", "pip", "distutils", "dev", "props"],
},
"default": {
"help": "development kit package",
"options": [
"stable",
"pip",
"distutils",
"tcltk",
"idle",
"tests",
"tools",
"venv",
"dev",
"symbols",
"bdist-wininst",
"chm",
],
},
"embed": {
"help": "embeddable package",
"options": ["stable", "zip-lib", "flat-dlls", "underpth", "precompile"],
},
}
@public
def get_argparse_options():
for opt, info in OPTIONS.items():
help = "When specified, includes {}".format(info["help"])
if info.get("not-in-all"):
help = "{}. Not affected by --include-all".format(help)
yield "--include-{}".format(opt), help
for opt, info in PRESETS.items():
help = "When specified, includes default options for {}".format(info["help"])
yield "--preset-{}".format(opt), help
def ns_get(ns, key, default=False):
return getattr(ns, key.replace("-", "_"), default)
def ns_set(ns, key, value=True):
k1 = key.replace("-", "_")
k2 = "include_{}".format(k1)
if hasattr(ns, k2):
setattr(ns, k2, value)
elif hasattr(ns, k1):
setattr(ns, k1, value)
else:
raise AttributeError("no argument named '{}'".format(k1))
@public
def update_presets(ns):
for preset, info in PRESETS.items():
if ns_get(ns, "preset-{}".format(preset)):
for opt in info["options"]:
ns_set(ns, opt)
if ns.include_all:
for opt in OPTIONS:
if OPTIONS[opt].get("not-in-all"):
continue
ns_set(ns, opt)

79
PC/layout/support/pip.py Normal file
View File

@ -0,0 +1,79 @@
"""
Extraction and file list generation for pip.
"""
__author__ = "Steve Dower <steve.dower@python.org>"
__version__ = "3.8"
import os
import shutil
import subprocess
import sys
__all__ = []
def public(f):
__all__.append(f.__name__)
return f
@public
def get_pip_dir(ns):
if ns.copy:
if ns.zip_lib:
return ns.copy / "packages"
return ns.copy / "Lib" / "site-packages"
else:
return ns.temp / "packages"
@public
def extract_pip_files(ns):
dest = get_pip_dir(ns)
dest.mkdir(parents=True, exist_ok=True)
src = ns.source / "Lib" / "ensurepip" / "_bundled"
ns.temp.mkdir(parents=True, exist_ok=True)
wheels = [shutil.copy(whl, ns.temp) for whl in src.glob("*.whl")]
search_path = os.pathsep.join(wheels)
if os.environ.get("PYTHONPATH"):
search_path += ";" + os.environ["PYTHONPATH"]
env = os.environ.copy()
env["PYTHONPATH"] = search_path
output = subprocess.check_output(
[
sys.executable,
"-m",
"pip",
"--no-color",
"install",
"pip",
"setuptools",
"--upgrade",
"--target",
str(dest),
"--no-index",
"--no-cache-dir",
"-f",
str(src),
"--only-binary",
":all:",
],
env=env,
)
try:
shutil.rmtree(dest / "bin")
except OSError:
pass
for file in wheels:
try:
os.remove(file)
except OSError:
pass

110
PC/layout/support/props.py Normal file
View File

@ -0,0 +1,110 @@
"""
Provides .props file.
"""
import os
from .constants import *
__all__ = ["PYTHON_PROPS_NAME"]
def public(f):
__all__.append(f.__name__)
return f
PYTHON_PROPS_NAME = "python.props"
PROPS_DATA = {
"PYTHON_TAG": VER_DOT,
"PYTHON_VERSION": os.getenv("PYTHON_NUSPEC_VERSION"),
"PYTHON_PLATFORM": os.getenv("PYTHON_PROPS_PLATFORM"),
"PYTHON_TARGET": "",
}
if not PROPS_DATA["PYTHON_VERSION"]:
if VER_NAME:
PROPS_DATA["PYTHON_VERSION"] = "{}.{}-{}{}".format(
VER_DOT, VER_MICRO, VER_NAME, VER_SERIAL
)
else:
PROPS_DATA["PYTHON_VERSION"] = "{}.{}".format(VER_DOT, VER_MICRO)
if not PROPS_DATA["PYTHON_PLATFORM"]:
PROPS_DATA["PYTHON_PLATFORM"] = "x64" if IS_X64 else "Win32"
PROPS_DATA["PYTHON_TARGET"] = "_GetPythonRuntimeFilesDependsOn{}{}_{}".format(
VER_MAJOR, VER_MINOR, PROPS_DATA["PYTHON_PLATFORM"]
)
PROPS_TEMPLATE = r"""<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="$(Platform) == '{PYTHON_PLATFORM}'">
<PythonHome Condition="$(Configuration) == 'Debug'">$([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python_d.exe")</PythonHome>
<PythonHome Condition="$(PythonHome) == ''">$([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python.exe")</PythonHome>
<PythonInclude>$(PythonHome)\include</PythonInclude>
<PythonLibs>$(PythonHome)\libs</PythonLibs>
<PythonTag>{PYTHON_TAG}</PythonTag>
<PythonVersion>{PYTHON_VERSION}</PythonVersion>
<IncludePythonExe Condition="$(IncludePythonExe) == ''">true</IncludePythonExe>
<IncludeDistutils Condition="$(IncludeDistutils) == ''">false</IncludeDistutils>
<IncludeLib2To3 Condition="$(IncludeLib2To3) == ''">false</IncludeLib2To3>
<IncludeVEnv Condition="$(IncludeVEnv) == ''">false</IncludeVEnv>
<GetPythonRuntimeFilesDependsOn>{PYTHON_TARGET};$(GetPythonRuntimeFilesDependsOn)</GetPythonRuntimeFilesDependsOn>
</PropertyGroup>
<ItemDefinitionGroup Condition="$(Platform) == '{PYTHON_PLATFORM}'">
<ClCompile>
<AdditionalIncludeDirectories>$(PythonInclude);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(PythonLibs);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<Target Name="GetPythonRuntimeFiles" Returns="@(PythonRuntime)" DependsOnTargets="$(GetPythonRuntimeFilesDependsOn)" />
<Target Name="{PYTHON_TARGET}" Returns="@(PythonRuntime)">
<ItemGroup>
<_PythonRuntimeExe Include="$(PythonHome)\python*.dll" />
<_PythonRuntimeExe Include="$(PythonHome)\python*.exe" Condition="$(IncludePythonExe) == 'true'" />
<_PythonRuntimeExe>
<Link>%(Filename)%(Extension)</Link>
</_PythonRuntimeExe>
<_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.pyd" />
<_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.dll" />
<_PythonRuntimeDlls>
<Link>DLLs\%(Filename)%(Extension)</Link>
</_PythonRuntimeDlls>
<_PythonRuntimeLib Include="$(PythonHome)\Lib\**\*" Exclude="$(PythonHome)\Lib\**\*.pyc;$(PythonHome)\Lib\site-packages\**\*" />
<_PythonRuntimeLib Remove="$(PythonHome)\Lib\distutils\**\*" Condition="$(IncludeDistutils) != 'true'" />
<_PythonRuntimeLib Remove="$(PythonHome)\Lib\lib2to3\**\*" Condition="$(IncludeLib2To3) != 'true'" />
<_PythonRuntimeLib Remove="$(PythonHome)\Lib\ensurepip\**\*" Condition="$(IncludeVEnv) != 'true'" />
<_PythonRuntimeLib Remove="$(PythonHome)\Lib\venv\**\*" Condition="$(IncludeVEnv) != 'true'" />
<_PythonRuntimeLib>
<Link>Lib\%(RecursiveDir)%(Filename)%(Extension)</Link>
</_PythonRuntimeLib>
<PythonRuntime Include="@(_PythonRuntimeExe);@(_PythonRuntimeDlls);@(_PythonRuntimeLib)" />
</ItemGroup>
<Message Importance="low" Text="Collected Python runtime from $(PythonHome):%0D%0A@(PythonRuntime->' %(Link)','%0D%0A')" />
</Target>
</Project>
"""
@public
def get_props_layout(ns):
if ns.include_all or ns.include_props:
yield "python.props", ns.temp / "python.props"
@public
def get_props(ns):
# TODO: Filter contents of props file according to included/excluded items
props = PROPS_TEMPLATE.format_map(PROPS_DATA)
return props.encode("utf-8")

View File

@ -7,6 +7,11 @@
#include <winuser.h> #include <winuser.h>
1 RT_MANIFEST "python.manifest" 1 RT_MANIFEST "python.manifest"
#if defined(PY_ICON)
1 ICON DISCARDABLE "icons\python.ico"
#elif defined(PYW_ICON)
1 ICON DISCARDABLE "icons\pythonw.ico"
#else
1 ICON DISCARDABLE "icons\launcher.ico" 1 ICON DISCARDABLE "icons\launcher.ico"
2 ICON DISCARDABLE "icons\py.ico" 2 ICON DISCARDABLE "icons\py.ico"
3 ICON DISCARDABLE "icons\pyc.ico" 3 ICON DISCARDABLE "icons\pyc.ico"
@ -14,6 +19,7 @@
5 ICON DISCARDABLE "icons\python.ico" 5 ICON DISCARDABLE "icons\python.ico"
6 ICON DISCARDABLE "icons\pythonw.ico" 6 ICON DISCARDABLE "icons\pythonw.ico"
7 ICON DISCARDABLE "icons\setup.ico" 7 ICON DISCARDABLE "icons\setup.ico"
#endif
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// //

226
PC/python_uwp.cpp Normal file
View File

@ -0,0 +1,226 @@
/* Main program when embedded in a UWP application on Windows */
#include "Python.h"
#include <string.h>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <shellapi.h>
#include <winrt\Windows.ApplicationModel.h>
#include <winrt\Windows.Storage.h>
#ifdef PYTHONW
#ifdef _DEBUG
const wchar_t *PROGNAME = L"pythonw_d.exe";
#else
const wchar_t *PROGNAME = L"pythonw.exe";
#endif
#else
#ifdef _DEBUG
const wchar_t *PROGNAME = L"python_d.exe";
#else
const wchar_t *PROGNAME = L"python.exe";
#endif
#endif
static void
set_user_base()
{
wchar_t envBuffer[2048];
try {
const auto appData = winrt::Windows::Storage::ApplicationData::Current();
if (appData) {
const auto localCache = appData.LocalCacheFolder();
if (localCache) {
auto path = localCache.Path();
if (!path.empty() &&
!wcscpy_s(envBuffer, path.c_str()) &&
!wcscat_s(envBuffer, L"\\local-packages")
) {
_wputenv_s(L"PYTHONUSERBASE", envBuffer);
}
}
}
} catch (...) {
}
}
static const wchar_t *
get_argv0(const wchar_t *argv0)
{
winrt::hstring installPath;
const wchar_t *launcherPath;
wchar_t *buffer;
size_t len;
launcherPath = _wgetenv(L"__PYVENV_LAUNCHER__");
if (launcherPath && launcherPath[0]) {
len = wcslen(launcherPath) + 1;
buffer = (wchar_t *)malloc(sizeof(wchar_t) * len);
if (!buffer) {
Py_FatalError("out of memory");
return NULL;
}
if (wcscpy_s(buffer, len, launcherPath)) {
Py_FatalError("failed to copy to buffer");
return NULL;
}
return buffer;
}
try {
const auto package = winrt::Windows::ApplicationModel::Package::Current();
if (package) {
const auto install = package.InstalledLocation();
if (install) {
installPath = install.Path();
}
}
}
catch (...) {
}
if (!installPath.empty()) {
len = installPath.size() + wcslen(PROGNAME) + 2;
} else {
len = wcslen(argv0) + wcslen(PROGNAME) + 1;
}
buffer = (wchar_t *)malloc(sizeof(wchar_t) * len);
if (!buffer) {
Py_FatalError("out of memory");
return NULL;
}
if (!installPath.empty()) {
if (wcscpy_s(buffer, len, installPath.c_str())) {
Py_FatalError("failed to copy to buffer");
return NULL;
}
if (wcscat_s(buffer, len, L"\\")) {
Py_FatalError("failed to concatenate backslash");
return NULL;
}
} else {
if (wcscpy_s(buffer, len, argv0)) {
Py_FatalError("failed to copy argv[0]");
return NULL;
}
wchar_t *name = wcsrchr(buffer, L'\\');
if (name) {
name[1] = L'\0';
} else {
buffer[0] = L'\0';
}
}
if (wcscat_s(buffer, len, PROGNAME)) {
Py_FatalError("failed to concatenate program name");
return NULL;
}
return buffer;
}
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) {
Py_FatalError("out of memory");
return NULL;
}
len = GetModuleFileNameW(NULL, r, bufferLen);
if (len == 0) {
free((void *)r);
return NULL;
} else if (len == bufferLen &&
GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
free(r);
r = NULL;
bufferLen *= 2;
}
}
return r;
}
int
wmain(int argc, wchar_t **argv)
{
const wchar_t **new_argv;
int new_argc;
const wchar_t *exeName;
new_argc = argc;
new_argv = (const wchar_t**)malloc(sizeof(wchar_t *) * (argc + 2));
if (new_argv == NULL) {
Py_FatalError("out of memory");
return -1;
}
exeName = get_process_name();
new_argv[0] = get_argv0(exeName ? exeName : argv[0]);
for (int i = 1; i < argc; ++i) {
new_argv[i] = argv[i];
}
set_user_base();
if (exeName) {
const wchar_t *p = wcsrchr(exeName, L'\\');
if (p) {
const wchar_t *moduleName = NULL;
if (*p++ == L'\\') {
if (wcsnicmp(p, L"pip", 3) == 0) {
moduleName = L"pip";
_wputenv_s(L"PIP_USER", L"true");
}
else if (wcsnicmp(p, L"idle", 4) == 0) {
moduleName = L"idlelib";
}
}
if (moduleName) {
new_argc += 2;
for (int i = argc; i >= 1; --i) {
new_argv[i + 2] = new_argv[i];
}
new_argv[1] = L"-m";
new_argv[2] = moduleName;
}
}
}
/* Override program_full_path from here so that
sys.executable is set correctly. */
_Py_SetProgramFullPath(new_argv[0]);
int result = Py_Main(new_argc, (wchar_t **)new_argv);
free((void *)exeName);
free((void *)new_argv);
return result;
}
#ifdef PYTHONW
int WINAPI wWinMain(
HINSTANCE hInstance, /* handle to current instance */
HINSTANCE hPrevInstance, /* handle to previous instance */
LPWSTR lpCmdLine, /* pointer to command line */
int nCmdShow /* show state of window */
)
{
return wmain(__argc, __wargv);
}
#endif

146
PC/store_info.txt Normal file
View File

@ -0,0 +1,146 @@
# Overview
NOTE: This file requires more content.
Since Python 3.8.2, releases have been made through the Microsoft Store
to allow easy installation on Windows 10.0.17763.0 and later.
# Building
To build the store package, the PC/layout script should be used.
Execute the directory with the build of Python to package, and pass
"-h" for full command-line options.
To sideload test builds, you will need a local certificate.
Instructions are available at
https://docs.microsoft.com/windows/uwp/packaging/create-certificate-package-signing.
After exporting your certificate, you will need the subject name and
SHA256 hash. The `certutil -dump <cert file>` command will display this
information.
To build for sideloading, use these commands in PowerShell:
```
$env:APPX_DATA_PUBLISHER=<your certificate subject name>
$env:APPX_DATA_SHA256=<your certificate SHA256>
$env:SigningCertificateFile=<your certificate file>
python PC/layout --copy <layout directory> --include-appxmanifest
Tools/msi/make_appx.ps1 <layout directory> python.msix -sign
Add-AppxPackage python.msix
```
(Note that only the last command requires PowerShell, and the others
can be used from Command Prompt. You can also double-click to install
the final package.)
To build for publishing to the Store, use these commands:
```
$env:APPX_DATA_PUBLISHER = $null
$env:APPX_DATA_SHA256 = $null
python PC/layout --copy <layout directory> --preset-appxmanifest --precompile
Tools/msi/make_appx.ps1 <layout directory> python.msix
```
Note that this package cannot be installed locally. It may only be
added to a submission for the store.
# Submission Metadata
This file contains the text that we use to fill out the store listing
for the Microsoft Store. It needs to be entered manually when creating
a new submission via the dashboard at
https://partner.microsoft.com/dashboard.
We keep it here for convenience and to allow it to be updated via pull
requests.
## Title
Python 3.8
## Short Title
Python
## Description
Python is an easy to learn, powerful programming language. It has efficient high-level data structures and a simple but effective approach to object-oriented programming. Pythons elegant syntax and dynamic typing, together with its interpreted nature, make it an ideal language for scripting and rapid application development in many areas on most platforms.
The Python interpreter and the extensive standard library are freely available in source or binary form for all major platforms from the Python Web site, https://www.python.org/, and may be freely distributed. The same site also contains distributions of and pointers to many free third party Python modules, programs and tools, and additional documentation.
The Python interpreter is easily extended with new functions and data types implemented in C or C++ (or other languages callable from C). Python is also suitable as an extension language for customizable applications.
## ShortDescription
The Python 3.8 interpreter and runtime.
## Copyright Trademark Information
(c) Python Software Foundation
## Additional License Terms
Visit https://docs.python.org/3.8/license.html for latest license terms.
PSF LICENSE AGREEMENT FOR PYTHON 3.8
1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and
the Individual or Organization ("Licensee") accessing and otherwise using Python
3.8 software in source or binary form and its associated documentation.
2. Subject to the terms and conditions of this License Agreement, PSF hereby
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
analyze, test, perform and/or display publicly, prepare derivative works,
distribute, and otherwise use Python 3.8 alone or in any derivative
version, provided, however, that PSF's License Agreement and PSF's notice of
copyright, i.e., "Copyright © 2001-2018 Python Software Foundation; All Rights
Reserved" are retained in Python 3.8 alone or in any derivative version
prepared by Licensee.
3. In the event Licensee prepares a derivative work that is based on or
incorporates Python 3.8 or any part thereof, and wants to make the
derivative work available to others as provided herein, then Licensee hereby
agrees to include in any such work a brief summary of the changes made to Python
3.8.
4. PSF is making Python 3.8 available to Licensee on an "AS IS" basis.
PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF
EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR
WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE
USE OF PYTHON 3.8 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.8
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF
MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.8, OR ANY DERIVATIVE
THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material breach of
its terms and conditions.
7. Nothing in this License Agreement shall be deemed to create any relationship
of agency, partnership, or joint venture between PSF and Licensee. This License
Agreement does not grant permission to use PSF trademarks or trade name in a
trademark sense to endorse or promote products or services of Licensee, or any
third party.
8. By copying, installing or otherwise using Python 3.8, Licensee agrees
to be bound by the terms and conditions of this License Agreement.
## Features
* Easy to install Python runtime
* Supported by core CPython team
* Find Python, Pip and Idle on PATH
## Search Terms
* Python
* Scripting
* Interpreter

View File

@ -95,4 +95,10 @@
<Target Name="_CleanTclTkDLL" BeforeTargets="Clean"> <Target Name="_CleanTclTkDLL" BeforeTargets="Clean">
<Delete Files="@(_TclTkDLL->'$(OutDir)%(Filename)%(Extension)')" /> <Delete Files="@(_TclTkDLL->'$(OutDir)%(Filename)%(Extension)')" />
</Target> </Target>
<Target Name="_WriteTCL_LIBRARY" Outputs="$(OutDir)TCL_LIBRARY.env" AfterTargets="Build">
<WriteLinesToFile File="$(OutDir)TCL_LIBRARY.env" Lines="$(tcltkdir)\lib\tcl$(TclMajorVersion).$(TclMinorVersion)" Encoding="utf-8" Overwrite="true" />
</Target>
<Target Name="_CleanTCL_LIBRARY" BeforeTargets="Clean">
<Delete Files="$(OutDir)TCL_LIBRARY.env" />
</Target>
</Project> </Project>

View File

@ -29,6 +29,16 @@
@where msbuild > "%TEMP%\msbuild.loc" 2> nul && set /P MSBUILD= < "%TEMP%\msbuild.loc" & del "%TEMP%\msbuild.loc" @where msbuild > "%TEMP%\msbuild.loc" 2> nul && set /P MSBUILD= < "%TEMP%\msbuild.loc" & del "%TEMP%\msbuild.loc"
@if exist "%MSBUILD%" set MSBUILD="%MSBUILD%" & (set _Py_MSBuild_Source=PATH) & goto :found @if exist "%MSBUILD%" set MSBUILD="%MSBUILD%" & (set _Py_MSBuild_Source=PATH) & goto :found
@rem VS 2017 and later provide vswhere.exe, which can be used
@if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto :skip_vswhere
@set _Py_MSBuild_Root=
@for /F "tokens=*" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -property installationPath -latest') DO @(set _Py_MSBuild_Root=%%i\MSBuild)
@if not defined _Py_MSBuild_Root goto :skip_vswhere
@for %%j in (Current 15.0) DO @if exist "%_Py_MSBuild_Root%\%%j\Bin\msbuild.exe" (set MSBUILD="%_Py_MSBuild_Root%\%%j\Bin\msbuild.exe")
@set _Py_MSBuild_Root=
@if defined MSBUILD @if exist %MSBUILD% (set _Py_MSBuild_Source=Visual Studio installation) & goto :found
:skip_vswhere
@rem VS 2017 sets exactly one install as the "main" install, so we may find MSBuild in there. @rem VS 2017 sets exactly one install as the "main" install, so we may find MSBuild in there.
@reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32 >nul 2>nul @reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32 >nul 2>nul
@if NOT ERRORLEVEL 1 @for /F "tokens=1,2*" %%i in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32') DO @( @if NOT ERRORLEVEL 1 @for /F "tokens=1,2*" %%i in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32') DO @(

View File

@ -52,6 +52,8 @@
<ExtensionModules Include="_asyncio;_contextvars;_ctypes;_decimal;_elementtree;_msi;_multiprocessing;_overlapped;pyexpat;_queue;select;unicodedata;winsound" /> <ExtensionModules Include="_asyncio;_contextvars;_ctypes;_decimal;_elementtree;_msi;_multiprocessing;_overlapped;pyexpat;_queue;select;unicodedata;winsound" />
<!-- Extension modules that require external sources --> <!-- Extension modules that require external sources -->
<ExternalModules Include="_bz2;_lzma;_sqlite3" /> <ExternalModules Include="_bz2;_lzma;_sqlite3" />
<!-- venv launchers -->
<Projects Include="venvlauncher.vcxproj;venvwlauncher.vcxproj" />
<!-- _ssl will build _socket as well, which may cause conflicts in parallel builds --> <!-- _ssl will build _socket as well, which may cause conflicts in parallel builds -->
<ExtensionModules Include="_socket" Condition="!$(IncludeSSL) or !$(IncludeExternals)" /> <ExtensionModules Include="_socket" Condition="!$(IncludeSSL) or !$(IncludeExternals)" />
<ExternalModules Include="_ssl;_hashlib" Condition="$(IncludeSSL)" /> <ExternalModules Include="_ssl;_hashlib" Condition="$(IncludeSSL)" />
@ -70,6 +72,7 @@
<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" />
<Projects2 Include="python_uwp.vcxproj;pythonw_uwp.vcxproj" />
</ItemGroup> </ItemGroup>
<Target Name="Build"> <Target Name="Build">

View File

@ -93,6 +93,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_queue", "_queue.vcxproj",
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "liblzma.vcxproj", "{12728250-16EC-4DC6-94D7-E21DD88947F8}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "liblzma.vcxproj", "{12728250-16EC-4DC6-94D7-E21DD88947F8}"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python_uwp", "python_uwp.vcxproj", "{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "venvlauncher", "venvlauncher.vcxproj", "{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "venvwlauncher", "venvwlauncher.vcxproj", "{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pythonw_uwp", "pythonw_uwp.vcxproj", "{AB603547-1E2A-45B3-9E09-B04596006393}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32 Debug|Win32 = Debug|Win32
@ -693,6 +701,70 @@ Global
{12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|Win32.Build.0 = Release|Win32 {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|Win32.Build.0 = Release|Win32
{12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.ActiveCfg = Release|x64 {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.ActiveCfg = Release|x64
{12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.Build.0 = Release|x64 {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.Build.0 = Release|x64
{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|Win32.ActiveCfg = Debug|Win32
{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|Win32.Build.0 = Debug|Win32
{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|x64.ActiveCfg = Debug|x64
{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|x64.Build.0 = Debug|x64
{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32
{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|Win32.Build.0 = PGInstrument|Win32
{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|x64.ActiveCfg = PGInstrument|x64
{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|x64.Build.0 = PGInstrument|x64
{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32
{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|Win32.Build.0 = PGUpdate|Win32
{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|x64.ActiveCfg = PGUpdate|x64
{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|x64.Build.0 = PGUpdate|x64
{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|Win32.ActiveCfg = Release|Win32
{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|Win32.Build.0 = Release|Win32
{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|x64.ActiveCfg = Release|x64
{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|x64.Build.0 = Release|x64
{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|Win32.ActiveCfg = Debug|Win32
{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|Win32.Build.0 = Debug|Win32
{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|x64.ActiveCfg = Debug|x64
{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|x64.Build.0 = Debug|x64
{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32
{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|Win32.Build.0 = PGInstrument|Win32
{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|x64.ActiveCfg = PGInstrument|x64
{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|x64.Build.0 = PGInstrument|x64
{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32
{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|Win32.Build.0 = PGUpdate|Win32
{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|x64.ActiveCfg = PGUpdate|x64
{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|x64.Build.0 = PGUpdate|x64
{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|Win32.ActiveCfg = Release|Win32
{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|Win32.Build.0 = Release|Win32
{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|x64.ActiveCfg = Release|x64
{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|x64.Build.0 = Release|x64
{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|Win32.ActiveCfg = Debug|Win32
{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|Win32.Build.0 = Debug|Win32
{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|x64.ActiveCfg = Debug|x64
{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|x64.Build.0 = Debug|x64
{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32
{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|Win32.Build.0 = PGInstrument|Win32
{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|x64.ActiveCfg = PGInstrument|x64
{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|x64.Build.0 = PGInstrument|x64
{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32
{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|Win32.Build.0 = PGUpdate|Win32
{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|x64.ActiveCfg = PGUpdate|x64
{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|x64.Build.0 = PGUpdate|x64
{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|Win32.ActiveCfg = Release|Win32
{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|Win32.Build.0 = Release|Win32
{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|x64.ActiveCfg = Release|x64
{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|x64.Build.0 = Release|x64
{AB603547-1E2A-45B3-9E09-B04596006393}.Debug|Win32.ActiveCfg = Debug|Win32
{AB603547-1E2A-45B3-9E09-B04596006393}.Debug|Win32.Build.0 = Debug|Win32
{AB603547-1E2A-45B3-9E09-B04596006393}.Debug|x64.ActiveCfg = Debug|x64
{AB603547-1E2A-45B3-9E09-B04596006393}.Debug|x64.Build.0 = Debug|x64
{AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32
{AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|Win32.Build.0 = PGInstrument|Win32
{AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|x64.ActiveCfg = PGInstrument|x64
{AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|x64.Build.0 = PGInstrument|x64
{AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32
{AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|Win32.Build.0 = PGUpdate|Win32
{AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|x64.ActiveCfg = PGUpdate|x64
{AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|x64.Build.0 = PGUpdate|x64
{AB603547-1E2A-45B3-9E09-B04596006393}.Release|Win32.ActiveCfg = Release|Win32
{AB603547-1E2A-45B3-9E09-B04596006393}.Release|Win32.Build.0 = Release|Win32
{AB603547-1E2A-45B3-9E09-B04596006393}.Release|x64.ActiveCfg = Release|x64
{AB603547-1E2A-45B3-9E09-B04596006393}.Release|x64.Build.0 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -77,7 +77,8 @@
--> -->
<_RegistryVersion>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion)</_RegistryVersion> <_RegistryVersion>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion)</_RegistryVersion>
<_RegistryVersion Condition="$(_RegistryVersion) == ''">$(Registry:HKEY_LOCAL_MACHINE\WOW6432Node\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion)</_RegistryVersion> <_RegistryVersion Condition="$(_RegistryVersion) == ''">$(Registry:HKEY_LOCAL_MACHINE\WOW6432Node\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion)</_RegistryVersion>
<DefaultWindowsSDKVersion>10.0.17134.0</DefaultWindowsSDKVersion> <DefaultWindowsSDKVersion>10.0.17763.0</DefaultWindowsSDKVersion>
<DefaultWindowsSDKVersion Condition="$(_RegistryVersion) == '10.0.17134'">10.0.17134.0</DefaultWindowsSDKVersion>
<DefaultWindowsSDKVersion Condition="$(_RegistryVersion) == '10.0.16299'">10.0.16299.0</DefaultWindowsSDKVersion> <DefaultWindowsSDKVersion Condition="$(_RegistryVersion) == '10.0.16299'">10.0.16299.0</DefaultWindowsSDKVersion>
<DefaultWindowsSDKVersion Condition="$(_RegistryVersion) == '10.0.15063'">10.0.15063.0</DefaultWindowsSDKVersion> <DefaultWindowsSDKVersion Condition="$(_RegistryVersion) == '10.0.15063'">10.0.15063.0</DefaultWindowsSDKVersion>
<DefaultWindowsSDKVersion Condition="$(_RegistryVersion) == '10.0.14393'">10.0.14393.0</DefaultWindowsSDKVersion> <DefaultWindowsSDKVersion Condition="$(_RegistryVersion) == '10.0.14393'">10.0.14393.0</DefaultWindowsSDKVersion>

View File

@ -0,0 +1,86 @@
<?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>{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}</ProjectGuid>
</PropertyGroup>
<Import Project="python.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<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" />
<PropertyGroup>
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalOptions>/EHsc /std:c++17 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>windowsapp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<None Include="..\PC\pycon.ico" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\PC\python_exe.rc" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\PC\python_uwp.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="pythoncore.vcxproj">
<Project>{cf7ac3d1-e2df-41d2-bea6-1e2556cdea26}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -479,4 +479,19 @@
<Target Name="_WarnAboutZlib" BeforeTargets="PrepareForBuild" Condition="!$(IncludeExternals)"> <Target Name="_WarnAboutZlib" BeforeTargets="PrepareForBuild" Condition="!$(IncludeExternals)">
<Warning Text="Not including zlib is not a supported configuration." /> <Warning Text="Not including zlib is not a supported configuration." />
</Target> </Target>
<PropertyGroup>
<VCRedistDir>$(VCInstallDir)\Redist\MSVC\$(VCToolsRedistVersion)\</VCRedistDir>
<VCRedistDir Condition="$(Platform) == 'Win32'">$(VCRedistDir)x86\</VCRedistDir>
<VCRedistDir Condition="$(Platform) != 'Win32'">$(VCRedistDir)$(Platform)\</VCRedistDir>
</PropertyGroup>
<ItemGroup Condition="$(VCInstallDir) != ''">
<VCRuntimeDLL Include="$(VCRedistDir)\**\vcruntime*.dll" />
</ItemGroup>
<Target Name="_CopyVCRuntime" AfterTargets="Build" Inputs="@(VCRuntimeDLL)" Outputs="$(OutDir)%(Filename)%(Extension)">
<Copy SourceFiles="%(VCRuntimeDLL.FullPath)" DestinationFolder="$(OutDir)" />
</Target>
<Target Name="_CleanVCRuntime" AfterTargets="Clean">
<Delete Files="@(VCRuntimeDLL->'$(OutDir)%(Filename)%(Extension)')" />
</Target>
</Project> </Project>

View File

@ -0,0 +1,86 @@
<?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>{AB603547-1E2A-45B3-9E09-B04596006393}</ProjectGuid>
</PropertyGroup>
<Import Project="python.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<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" />
<PropertyGroup>
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>PYTHONW;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalOptions>/EHsc /std:c++17 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>windowsapp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<None Include="..\PC\pyconw.ico" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\PC\pythonw_exe.rc" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\PC\python_uwp.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="pythoncore.vcxproj">
<Project>{cf7ac3d1-e2df-41d2-bea6-1e2556cdea26}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</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>{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

@ -37,6 +37,7 @@ set BUILDX64=
set TARGET=Rebuild set TARGET=Rebuild
set TESTTARGETDIR= set TESTTARGETDIR=
set PGO=-m test -q --pgo set PGO=-m test -q --pgo
set BUILDMSI=1
set BUILDNUGET=1 set BUILDNUGET=1
set BUILDZIP=1 set BUILDZIP=1
@ -61,6 +62,7 @@ if "%1" EQU "--pgo" (set PGO=%~2) && shift && shift && goto CheckOpts
if "%1" EQU "--skip-pgo" (set PGO=) && shift && goto CheckOpts if "%1" EQU "--skip-pgo" (set PGO=) && shift && goto CheckOpts
if "%1" EQU "--skip-nuget" (set BUILDNUGET=) && shift && goto CheckOpts if "%1" EQU "--skip-nuget" (set BUILDNUGET=) && shift && goto CheckOpts
if "%1" EQU "--skip-zip" (set BUILDZIP=) && shift && goto CheckOpts if "%1" EQU "--skip-zip" (set BUILDZIP=) && shift && goto CheckOpts
if "%1" EQU "--skip-msi" (set BUILDMSI=) && shift && goto CheckOpts
if "%1" NEQ "" echo Invalid option: "%1" && exit /B 1 if "%1" NEQ "" echo Invalid option: "%1" && exit /B 1
@ -174,10 +176,12 @@ if "%OUTDIR_PLAT%" EQU "win32" (
) )
set BUILDOPTS=/p:Platform=%1 /p:BuildForRelease=true /p:DownloadUrl=%DOWNLOAD_URL% /p:DownloadUrlBase=%DOWNLOAD_URL_BASE% /p:ReleaseUri=%RELEASE_URI% set BUILDOPTS=/p:Platform=%1 /p:BuildForRelease=true /p:DownloadUrl=%DOWNLOAD_URL% /p:DownloadUrlBase=%DOWNLOAD_URL_BASE% /p:ReleaseUri=%RELEASE_URI%
if defined BUILDMSI (
%MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true %MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true
if errorlevel 1 exit /B if errorlevel 1 exit /B
%MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false %MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false
if errorlevel 1 exit /B if errorlevel 1 exit /B
)
if defined BUILDZIP ( if defined BUILDZIP (
%MSBUILD% "%D%make_zip.proj" /t:Build %BUILDOPTS% %CERTOPTS% /p:OutputPath="%BUILD%en-us" %MSBUILD% "%D%make_zip.proj" /t:Build %BUILDOPTS% %CERTOPTS% /p:OutputPath="%BUILD%en-us"
@ -214,6 +218,7 @@ echo --skip-build (-B) Do not build Python (just do the installers)
echo --skip-doc (-D) Do not build documentation echo --skip-doc (-D) Do not build documentation
echo --pgo Specify PGO command for x64 installers echo --pgo Specify PGO command for x64 installers
echo --skip-pgo Build x64 installers without using PGO echo --skip-pgo Build x64 installers without using PGO
echo --skip-msi Do not build executable/MSI packages
echo --skip-nuget Do not build Nuget packages echo --skip-nuget Do not build Nuget packages
echo --skip-zip Do not build embeddable package echo --skip-zip Do not build embeddable package
echo --download Specify the full download URL for MSIs echo --download Specify the full download URL for MSIs

71
Tools/msi/make_appx.ps1 Normal file
View File

@ -0,0 +1,71 @@
<#
.Synopsis
Compiles and signs an APPX package
.Description
Given the file listing, ensures all the contents are signed
and builds and signs the final package.
.Parameter mapfile
The location on disk of the text mapping file.
.Parameter msix
The path and name to store the APPX/MSIX.
.Parameter sign
When set, signs the APPX/MSIX. Packages to be published to
the store should not be signed.
.Parameter description
Description to embed in the signature (optional).
.Parameter certname
The name of the certificate to sign with (optional).
.Parameter certsha1
The SHA1 hash of the certificate to sign with (optional).
#>
param(
[Parameter(Mandatory=$true)][string]$layout,
[Parameter(Mandatory=$true)][string]$msix,
[switch]$sign,
[string]$description,
[string]$certname,
[string]$certsha1,
[string]$certfile
)
$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent;
Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force
Set-Alias makeappx (Find-Tool "makeappx.exe") -Scope Script
Set-Alias makepri (Find-Tool "makepri.exe") -Scope Script
$msixdir = Split-Path $msix -Parent
if ($msixdir) {
$msixdir = (mkdir -Force $msixdir).FullName
} else {
$msixdir = Get-Location
}
$msix = Join-Path $msixdir (Split-Path $msix -Leaf)
pushd $layout
try {
if (Test-Path resources.pri) {
del resources.pri
}
$name = ([xml](gc AppxManifest.xml)).Package.Identity.Name
makepri new /pr . /mn AppxManifest.xml /in $name /cf _resources.xml /of _resources.pri /mf appx /o
if (-not $? -or -not (Test-Path _resources.map.txt)) {
throw "makepri step failed"
}
$lines = gc _resources.map.txt
$lines | ?{ -not ($_ -match '"_resources[\w\.]+?"') } | Out-File _resources.map.txt -Encoding utf8
makeappx pack /f _resources.map.txt /m AppxManifest.xml /o /p $msix
if (-not $?) {
throw "makeappx step failed"
}
} finally {
popd
}
if ($sign) {
Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files $msix
if (-not $?) {
throw "Package signing failed"
}
}

34
Tools/msi/make_cat.ps1 Normal file
View File

@ -0,0 +1,34 @@
<#
.Synopsis
Compiles and signs a catalog file.
.Description
Given the CDF definition file, builds and signs a catalog.
.Parameter catalog
The path to the catalog definition file to compile and
sign. It is assumed that the .cat file will be the same
name with a new extension.
.Parameter description
The description to add to the signature (optional).
.Parameter certname
The name of the certificate to sign with (optional).
.Parameter certsha1
The SHA1 hash of the certificate to sign with (optional).
#>
param(
[Parameter(Mandatory=$true)][string]$catalog,
[string]$description,
[string]$certname,
[string]$certsha1,
[string]$certfile
)
$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent;
Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force
Set-Alias MakeCat (Find-Tool "makecat.exe") -Scope Script
MakeCat $catalog
if (-not $?) {
throw "Catalog compilation failed"
}
Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files @($catalog -replace 'cdf$', 'cat')

View File

@ -15,11 +15,12 @@
<TargetExt>.zip</TargetExt> <TargetExt>.zip</TargetExt>
<TargetPath>$(OutputPath)\$(TargetName)$(TargetExt)</TargetPath> <TargetPath>$(OutputPath)\$(TargetName)$(TargetExt)</TargetPath>
<CleanCommand>rmdir /q/s "$(IntermediateOutputPath)\zip_$(ArchName)"</CleanCommand> <CleanCommand>rmdir /q/s "$(IntermediateOutputPath)\zip_$(ArchName)"</CleanCommand>
<Arguments>"$(PythonExe)" "$(MSBuildThisFileDirectory)\make_zip.py"</Arguments> <Arguments>"$(PythonExe)" "$(PySourcePath)PC\layout"</Arguments>
<Arguments>$(Arguments) -e -o "$(TargetPath)" -t "$(IntermediateOutputPath)\zip_$(ArchName)" -b "$(BuildPath.TrimEnd(`\`))"</Arguments> <Arguments>$(Arguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))"</Arguments>
<Environment>set DOC_FILENAME=python$(PythonVersion).chm</Environment> <Arguments>$(Arguments) -t "$(IntermediateOutputPath)\zip_$(ArchName)"</Arguments>
<Arguments>$(Arguments) --zip "$(TargetPath)"</Arguments>
<Arguments>$(Arguments) --precompile --zip-lib --include-underpth --include-stable --flat-dlls</Arguments>
<Environment>$(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib</Environment> <Environment>$(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib</Environment>
<Environment Condition="Exists($(CRTRedist))">$(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform)</Environment>
</PropertyGroup> </PropertyGroup>
<Target Name="_Build"> <Target Name="_Build">

View File

@ -1,250 +0,0 @@
import argparse
import py_compile
import re
import sys
import shutil
import stat
import os
import tempfile
from itertools import chain
from pathlib import Path
from zipfile import ZipFile, ZIP_DEFLATED
TKTCL_RE = re.compile(r'^(_?tk|tcl).+\.(pyd|dll)', re.IGNORECASE)
DEBUG_RE = re.compile(r'_d\.(pyd|dll|exe|pdb|lib)$', re.IGNORECASE)
PYTHON_DLL_RE = re.compile(r'python\d\d?\.dll$', re.IGNORECASE)
DEBUG_FILES = {
'_ctypes_test',
'_testbuffer',
'_testcapi',
'_testconsole',
'_testimportmultiple',
'_testmultiphase',
'xxlimited',
'python3_dstub',
}
EXCLUDE_FROM_LIBRARY = {
'__pycache__',
'idlelib',
'pydoc_data',
'site-packages',
'tkinter',
'turtledemo',
}
EXCLUDE_FROM_EMBEDDABLE_LIBRARY = {
'ensurepip',
'venv',
}
EXCLUDE_FILE_FROM_LIBRARY = {
'bdist_wininst.py',
}
EXCLUDE_FILE_FROM_LIBS = {
'liblzma',
'python3stub',
}
EXCLUDED_FILES = {
'pyshellext',
}
def is_not_debug(p):
if DEBUG_RE.search(p.name):
return False
if TKTCL_RE.search(p.name):
return False
return p.stem.lower() not in DEBUG_FILES and p.stem.lower() not in EXCLUDED_FILES
def is_not_debug_or_python(p):
return is_not_debug(p) and not PYTHON_DLL_RE.search(p.name)
def include_in_lib(p):
name = p.name.lower()
if p.is_dir():
if name in EXCLUDE_FROM_LIBRARY:
return False
if name == 'test' and p.parts[-2].lower() == 'lib':
return False
if name in {'test', 'tests'} and p.parts[-3].lower() == 'lib':
return False
return True
if name in EXCLUDE_FILE_FROM_LIBRARY:
return False
suffix = p.suffix.lower()
return suffix not in {'.pyc', '.pyo', '.exe'}
def include_in_embeddable_lib(p):
if p.is_dir() and p.name.lower() in EXCLUDE_FROM_EMBEDDABLE_LIBRARY:
return False
return include_in_lib(p)
def include_in_libs(p):
if not is_not_debug(p):
return False
return p.stem.lower() not in EXCLUDE_FILE_FROM_LIBS
def include_in_tools(p):
if p.is_dir() and p.name.lower() in {'scripts', 'i18n', 'pynche', 'demo', 'parser'}:
return True
return p.suffix.lower() in {'.py', '.pyw', '.txt'}
BASE_NAME = 'python{0.major}{0.minor}'.format(sys.version_info)
FULL_LAYOUT = [
('/', '$build', 'python.exe', is_not_debug),
('/', '$build', 'pythonw.exe', is_not_debug),
('/', '$build', 'python{}.dll'.format(sys.version_info.major), is_not_debug),
('/', '$build', '{}.dll'.format(BASE_NAME), is_not_debug),
('DLLs/', '$build', '*.pyd', is_not_debug),
('DLLs/', '$build', '*.dll', is_not_debug_or_python),
('include/', 'include', '*.h', None),
('include/', 'PC', 'pyconfig.h', None),
('Lib/', 'Lib', '**/*', include_in_lib),
('libs/', '$build', '*.lib', include_in_libs),
('Tools/', 'Tools', '**/*', include_in_tools),
]
EMBED_LAYOUT = [
('/', '$build', 'python*.exe', is_not_debug),
('/', '$build', '*.pyd', is_not_debug),
('/', '$build', '*.dll', is_not_debug),
('{}.zip'.format(BASE_NAME), 'Lib', '**/*', include_in_embeddable_lib),
]
if os.getenv('DOC_FILENAME'):
FULL_LAYOUT.append(('Doc/', 'Doc/build/htmlhelp', os.getenv('DOC_FILENAME'), None))
if os.getenv('VCREDIST_PATH'):
FULL_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None))
EMBED_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None))
def copy_to_layout(target, rel_sources):
count = 0
if target.suffix.lower() == '.zip':
if target.exists():
target.unlink()
with ZipFile(str(target), 'w', ZIP_DEFLATED) as f:
with tempfile.TemporaryDirectory() as tmpdir:
for s, rel in rel_sources:
if rel.suffix.lower() == '.py':
pyc = Path(tmpdir) / rel.with_suffix('.pyc').name
try:
py_compile.compile(str(s), str(pyc), str(rel), doraise=True, optimize=2)
except py_compile.PyCompileError:
f.write(str(s), str(rel))
else:
f.write(str(pyc), str(rel.with_suffix('.pyc')))
else:
f.write(str(s), str(rel))
count += 1
else:
for s, rel in rel_sources:
dest = target / rel
try:
dest.parent.mkdir(parents=True)
except FileExistsError:
pass
if dest.is_file():
dest.chmod(stat.S_IWRITE)
shutil.copy(str(s), str(dest))
if dest.is_file():
dest.chmod(stat.S_IWRITE)
count += 1
return count
def rglob(root, pattern, condition):
dirs = [root]
recurse = pattern[:3] in {'**/', '**\\'}
while dirs:
d = dirs.pop(0)
for f in d.glob(pattern[3:] if recurse else pattern):
if recurse and f.is_dir() and (not condition or condition(f)):
dirs.append(f)
elif f.is_file() and (not condition or condition(f)):
yield f, f.relative_to(root)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-s', '--source', metavar='dir', help='The directory containing the repository root', type=Path)
parser.add_argument('-o', '--out', metavar='file', help='The name of the output archive', type=Path, default=None)
parser.add_argument('-t', '--temp', metavar='dir', help='A directory to temporarily extract files into', type=Path, default=None)
parser.add_argument('-e', '--embed', help='Create an embedding layout', action='store_true', default=False)
parser.add_argument('-b', '--build', help='Specify the build directory', type=Path, default=None)
ns = parser.parse_args()
source = ns.source or (Path(__file__).resolve().parent.parent.parent)
out = ns.out
build = ns.build or Path(sys.exec_prefix)
assert isinstance(source, Path)
assert not out or isinstance(out, Path)
assert isinstance(build, Path)
if ns.temp:
temp = ns.temp
delete_temp = False
else:
temp = Path(tempfile.mkdtemp())
delete_temp = True
if out:
try:
out.parent.mkdir(parents=True)
except FileExistsError:
pass
try:
temp.mkdir(parents=True)
except FileExistsError:
pass
layout = EMBED_LAYOUT if ns.embed else FULL_LAYOUT
try:
for t, s, p, c in layout:
if s == '$build':
fs = build
else:
fs = source / s
files = rglob(fs, p, c)
extra_files = []
if s == 'Lib' and p == '**/*':
extra_files.append((
source / 'tools' / 'msi' / 'distutils.command.bdist_wininst.py',
Path('distutils') / 'command' / 'bdist_wininst.py'
))
copied = copy_to_layout(temp / t.rstrip('/'), chain(files, extra_files))
print('Copied {} files'.format(copied))
if ns.embed:
with open(str(temp / (BASE_NAME + '._pth')), 'w') as f:
print(BASE_NAME + '.zip', file=f)
print('.', file=f)
print('', file=f)
print('# Uncomment to run site.main() automatically', file=f)
print('#import site', file=f)
if out:
total = copy_to_layout(out, rglob(temp, '**/*', None))
print('Wrote {} files to {}'.format(total, out))
finally:
if delete_temp:
shutil.rmtree(temp, True)
if __name__ == "__main__":
sys.exit(int(main() or 0))

43
Tools/msi/sdktools.psm1 Normal file
View File

@ -0,0 +1,43 @@
function Find-Tool {
param([string]$toolname)
$kitroot = (gp 'HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots\').KitsRoot10
$tool = (gci -r "$kitroot\Bin\*\x64\$toolname" | sort FullName -Desc | select -First 1)
if (-not $tool) {
throw "$toolname is not available"
}
Write-Host "Found $toolname at $($tool.FullName)"
return $tool.FullName
}
Set-Alias SignTool (Find-Tool "signtool.exe") -Scope Script
function Sign-File {
param([string]$certname, [string]$certsha1, [string]$certfile, [string]$description, [string[]]$files)
if (-not $description) {
$description = $env:SigningDescription;
if (-not $description) {
$description = "Python";
}
}
if (-not $certname) {
$certname = $env:SigningCertificate;
}
if (-not $certfile) {
$certfile = $env:SigningCertificateFile;
}
foreach ($a in $files) {
if ($certsha1) {
SignTool sign /sha1 $certsha1 /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a
} elseif ($certname) {
SignTool sign /n $certname /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a
} elseif ($certfile) {
SignTool sign /f $certfile /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a
} else {
SignTool sign /a /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a
}
}
}

34
Tools/msi/sign_build.ps1 Normal file
View File

@ -0,0 +1,34 @@
<#
.Synopsis
Recursively signs the contents of a directory.
.Description
Given the file patterns, code signs the contents.
.Parameter root
The root directory to sign.
.Parameter patterns
The file patterns to sign
.Parameter description
The description to add to the signature (optional).
.Parameter certname
The name of the certificate to sign with (optional).
.Parameter certsha1
The SHA1 hash of the certificate to sign with (optional).
#>
param(
[Parameter(Mandatory=$true)][string]$root,
[string[]]$patterns=@("*.exe", "*.dll", "*.pyd"),
[string]$description,
[string]$certname,
[string]$certsha1,
[string]$certfile
)
$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent;
Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force
pushd $root
try {
Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files (gci -r $patterns)
} finally {
popd
}

View File

@ -20,25 +20,28 @@
<SignOutput>false</SignOutput> <SignOutput>false</SignOutput>
<TargetName>$(OutputName).$(NuspecVersion)</TargetName> <TargetName>$(OutputName).$(NuspecVersion)</TargetName>
<TargetExt>.nupkg</TargetExt> <TargetExt>.nupkg</TargetExt>
<IntermediateOutputPath>$(IntermediateOutputPath)\nuget_$(ArchName)</IntermediateOutputPath> <IntermediateOutputPath>$(IntermediateOutputPath)\nuget_$(ArchName)\</IntermediateOutputPath>
<CleanCommand>rmdir /q/s "$(IntermediateOutputPath)"</CleanCommand> <CleanCommand>rmdir /q/s "$(IntermediateOutputPath.TrimEnd(`\`))"</CleanCommand>
<PythonArguments>"$(PythonExe)" "$(MSBuildThisFileDirectory)\..\msi\make_zip.py"</PythonArguments> <PythonArguments>"$(PythonExe)" "$(PySourcePath)PC\layout"</PythonArguments>
<PythonArguments>$(PythonArguments) -t "$(IntermediateOutputPath)" -b "$(BuildPath.TrimEnd(`\`))"</PythonArguments> <PythonArguments>$(PythonArguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))"</PythonArguments>
<PythonArguments>$(PythonArguments) -t "$(IntermediateOutputPath)obj"</PythonArguments>
<PythonArguments>$(PythonArguments) --copy "$(IntermediateOutputPath)pkg"</PythonArguments>
<PythonArguments>$(PythonArguments) --include-dev --include-tools --include-pip --include-stable --include-launcher --include-props</PythonArguments>
<PipArguments>"$(IntermediateOutputPath)\python.exe" -B -c "import sys; sys.path.append(r'$(PySourcePath)\Lib'); import ensurepip; ensurepip._main()"</PipArguments> <PackageArguments Condition="$(Packages) != ''">"$(IntermediateOutputPath)pkg\pip.exe" -B -m pip install -U $(Packages)</PackageArguments>
<PackageArguments Condition="$(Packages) != ''">"$(IntermediateOutputPath)\python.exe" -B -m pip install -U $(Packages)</PackageArguments>
<NugetPackCommand>"$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)"</NugetPackCommand> <NugetPackCommand>"$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)pkg"</NugetPackCommand>
<NugetPackSymbolsCommand Condition="Exists('$(MSBuildThisFileDirectory)\$(OutputName).symbols.nuspec')">"$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).symbols.nuspec" -BasePath "$(BuildPath.TrimEnd(`\`))"</NugetPackSymbolsCommand> <NugetPackSymbolsCommand Condition="Exists('$(MSBuildThisFileDirectory)\$(OutputName).symbols.nuspec')">"$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).symbols.nuspec" -BasePath "$(BuildPath.TrimEnd(`\`))"</NugetPackSymbolsCommand>
<NugetArguments>$(NugetArguments) -OutputDirectory "$(OutputPath.Trim(`\`))"</NugetArguments> <NugetArguments>$(NugetArguments) -OutputDirectory "$(OutputPath.Trim(`\`))"</NugetArguments>
<NugetArguments>$(NugetArguments) -Version "$(NuspecVersion)"</NugetArguments> <NugetArguments>$(NugetArguments) -Version "$(NuspecVersion)"</NugetArguments>
<NugetArguments>$(NugetArguments) -NoPackageAnalysis -NonInteractive</NugetArguments> <NugetArguments>$(NugetArguments) -NoPackageAnalysis -NonInteractive</NugetArguments>
<Environment>set DOC_FILENAME=python$(PythonVersion).chm</Environment>
<Environment>$(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib</Environment> <Environment>$(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib</Environment>
<Environment Condition="Exists($(CRTRedist))">$(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform)</Environment> <Environment>$(Environment)%0D%0Aset PYTHON_NUSPEC_VERSION=$(NuspecVersion)</Environment>
<Environment Condition="$(Platform) != 'x86'">$(Environment)%0D%0Aset PYTHON_PROPS_PLATFORM=$(Platform)</Environment>
<Environment Condition="$(Platform) == 'x86'">$(Environment)%0D%0Aset PYTHON_PROPS_PLATFORM=Win32</Environment>
<Environment>$(Environment)%0D%0Amkdir "$(OutputPath.Trim(`\`))" &gt;nul 2&gt;nul</Environment> <Environment>$(Environment)%0D%0Amkdir "$(OutputPath.Trim(`\`))" &gt;nul 2&gt;nul</Environment>
</PropertyGroup> </PropertyGroup>
@ -48,22 +51,7 @@
<Target Name="_Build"> <Target Name="_Build">
<Exec Command="$(CleanCommand)" /> <Exec Command="$(CleanCommand)" />
<Exec Command="setlocal%0D%0A$(Environment)%0D%0A$(PythonArguments)" /> <Exec Command="setlocal%0D%0A$(Environment)%0D%0A$(PythonArguments)%0D%0A$(PackageArguments)" />
<Exec Command="$(PipArguments)" />
<Exec Command="$(PackageArguments)" Condition="$(PackageArguments) != ''" />
<PropertyGroup>
<_PropsContents>$([System.IO.File]::ReadAllText('python.props'))</_PropsContents>
<_PropsContents>$(_PropsContents.Replace('$$PYTHON_TAG$$', '$(MajorVersionNumber).$(MinorVersionNumber)'))</_PropsContents>
<_PropsContents>$(_PropsContents.Replace('$$PYTHON_VERSION$$', '$(NuspecVersion)'))</_PropsContents>
<_PropsContents Condition="$(Platform) == 'x86'">$(_PropsContents.Replace('$$PYTHON_PLATFORM$$', 'Win32'))</_PropsContents>
<_PropsContents Condition="$(Platform) != 'x86'">$(_PropsContents.Replace('$$PYTHON_PLATFORM$$', '$(Platform)'))</_PropsContents>
<_PropsContents>$(_PropsContents.Replace('$$PYTHON_TARGET$$', '_GetPythonRuntimeFilesDependsOn$(MajorVersionNumber)$(MinorVersionNumber)_$(Platform)'))</_PropsContents>
<_ExistingContents Condition="Exists('$(IntermediateOutputPath)\python.props')">$([System.IO.File]::ReadAllText('$(IntermediateOutputPath)\python.props'))</_ExistingContents>
</PropertyGroup>
<WriteLinesToFile File="$(IntermediateOutputPath)\python.props"
Lines="$(_PropsContents)"
Condition="$(_PropsContents) != $(_ExistingContents)" />
<Exec Command="$(NugetPackCommand) $(NugetArguments)" /> <Exec Command="$(NugetPackCommand) $(NugetArguments)" />
<Exec Command="$(NugetPackSymbolsCommand) $(NugetArguments)" Condition="$(NugetPackSymbolsCommand) != ''" /> <Exec Command="$(NugetPackSymbolsCommand) $(NugetArguments)" Condition="$(NugetPackSymbolsCommand) != ''" />