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

Also adds the PC/layout script for generating layouts on Windows.
This commit is contained in:
Steve Dower 2018-12-10 18:52:57 -08:00 committed by GitHub
parent 1c3de541e6
commit 0cd6391fd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 2928 additions and 405 deletions

View File

@ -0,0 +1,67 @@
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'
env:
IncludeUwp: true
- 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()

View File

@ -13,6 +13,8 @@ steps:
- script: PCbuild\build.bat -e $(buildOpt)
displayName: 'Build CPython'
env:
IncludeUwp: true
- script: python.bat -m test.pythoninfo
displayName: 'Display build info'

1
.gitattributes vendored
View File

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

View File

@ -115,12 +115,16 @@ goto end
:build
if not exist "%BUILDDIR%" mkdir "%BUILDDIR%"
rem We ought to move NEWS to %BUILDDIR%\NEWS and point
rem Sphinx at the right location.
if exist ..\Misc\NEWS (
echo.Copying Misc\NEWS to build\NEWS
if not exist build mkdir build
copy ..\Misc\NEWS build\NEWS > nul
) else if exist ..\Misc\NEWS.D (
if defined BLURB (
echo.Merging Misc/NEWS with %BLURB%
if not exist build mkdir build
%BLURB% merge -f build\NEWS
) else (
echo.No Misc/NEWS file and Blurb is not available.

View File

@ -12,9 +12,6 @@
This document aims to give an overview of Windows-specific behaviour you should
know about when using Python on Microsoft Windows.
Installing Python
=================
Unlike most Unix systems and services, Windows does not include a system
supported installation of Python. To make Python available, the CPython team
has compiled Windows installers (MSI packages) with every `release
@ -24,15 +21,37 @@ core interpreter and library being used by a single user. The installer is also
able to install for all users of a single machine, and a separate ZIP file is
available for application-local distributions.
Supported Versions
------------------
As specified in :pep:`11`, a Python release only supports a Windows platform
while Microsoft considers the platform under extended support. This means that
Python |version| supports Windows Vista and newer. If you require Windows XP
support then please install Python 3.4.
Installation Steps
There are a number of different installers available for Windows, each with
certain benefits and downsides.
:ref:`windows-full` contains all components and is the best option for
developers using Python for any kind of project.
:ref:`windows-store` is a simple installation of Python that is suitable for
running scripts and packages, and using IDLE or other development environments.
It requires Windows 10, but can be safely installed without corrupting other
programs. It also provides many convenient commands for launching Python and
its tools.
:ref:`windows-nuget` are lightweight installations intended for continuous
integration systems. It can be used to build Python packages or run scripts,
but is not updateable and has no user interface tools.
:ref:`windows-embeddable` is a minimal package of Python suitable for
embedding into a larger application.
.. _windows-full:
The full installer
==================
Installation steps
------------------
Four Python |version| installers are available for download - two each for the
@ -264,39 +283,199 @@ settings and replace any that have been removed or modified.
"Uninstall" will remove Python entirely, with the exception of the
:ref:`launcher`, which has its own entry in Programs and Features.
Other Platforms
---------------
With ongoing development of Python, some platforms that used to be supported
earlier are no longer supported (due to the lack of users or developers).
Check :pep:`11` for details on all unsupported platforms.
.. _windows-store:
* `Windows CE <http://pythonce.sourceforge.net/>`_ is still supported.
* The `Cygwin <https://cygwin.com/>`_ installer offers to install the Python
interpreter as well (cf. `Cygwin package source
<ftp://ftp.uni-erlangen.de/pub/pc/gnuwin32/cygwin/mirrors/cygnus/
release/python>`_, `Maintainer releases
<http://www.tishler.net/jason/software/python/>`_)
The Microsoft Store package
===========================
See `Python for Windows <https://www.python.org/downloads/windows/>`_
for detailed information about platforms with pre-compiled installers.
.. versionadded:: 3.7.2
.. seealso::
.. note::
The Microsoft Store package is currently considered unstable while its
interactions with other tools and other copies of Python are evaluated.
While Python itself is stable, this installation method may change its
behavior and capabilities during Python 3.7 releases.
`Python on XP <http://dooling.com/index.php/2006/03/14/python-on-xp-7-minutes-to-hello-world/>`_
"7 Minutes to "Hello World!""
by Richard Dooling, 2006
The Microsoft Store package is an easily installable Python interpreter that
is intended mainly for interactive use, for example, by students.
`Installing on Windows <http://www.diveintopython.net/installing_python/windows.html>`_
in "`Dive into Python: Python from novice to pro
<http://www.diveintopython.net/>`_"
by Mark Pilgrim, 2004,
ISBN 1-59059-356-1
To install the package, ensure you have the latest Windows 10 updates and
search the Microsoft Store app for "Python |version|". Ensure that the app
you select is published by the Python Software Foundation, and install it.
`For Windows users <https://python.swaroopch.com/installation.html#installation-on-windows>`_
in "Installing Python"
in "`A Byte of Python <https://python.swaroopch.com/>`_"
by Swaroop C H, 2003
.. warning::
Python will always be available for free on the Microsoft Store. If you
are asked to pay for it, you have not selected the correct package.
After installation, Python may be launched by finding it in Start.
Alternatively, it will be available from any Command Prompt or PowerShell
session by typing ``python``. Further, pip and IDLE may be used by typing
``pip`` or ``idle``. IDLE can also be found in Start.
All three commands are also available with version number suffixes, for
example, as ``python3.exe`` and ``python3.x.exe`` as well as
``python.exe`` (where ``3.x`` is the specific version you want to launch,
such as |version|).
Virtual environments can be created with ``python -m venv`` and activated
and used as normal.
If you have installed another version of Python and added it to your
``PATH`` variable, it will be available as ``python.exe`` rather than the
one from the Microsoft Store. To access the new installation, use
``python3.exe`` or ``python3.x.exe``.
To remove Python, open Settings and use Apps and Features, or else find
Python in Start and right-click to select Uninstall. Uninstalling will
remove all packages you installed directly into this Python installation, but
will not remove any virtual environments
Known Issues
------------
Currently, the ``py.exe`` launcher cannot be used to start Python when it
has been installed from the Microsoft Store.
Because of restrictions on Microsoft Store apps, Python scripts may not have
full write access to shared locations such as ``TEMP`` and the registry.
Instead, it will write to a private copy. If your scripts must modify the
shared locations, you will need to install the full installer.
.. _windows-nuget:
The nuget.org packages
======================
.. versionadded:: 3.5.2
The nuget.org package is a reduced size Python environment intended for use on
continuous integration and build systems that do not have a system-wide
install of Python. While nuget is "the package manager for .NET", it also works
perfectly fine for packages containing build-time tools.
Visit `nuget.org <https://www.nuget.org/>`_ for the most up-to-date information
on using nuget. What follows is a summary that is sufficient for Python
developers.
The ``nuget.exe`` command line tool may be downloaded directly from
``https://aka.ms/nugetclidl``, for example, using curl or PowerShell. With the
tool, the latest version of Python for 64-bit or 32-bit machines is installed
using::
nuget.exe install python -ExcludeVersion -OutputDirectory .
nuget.exe install pythonx86 -ExcludeVersion -OutputDirectory .
To select a particular version, add a ``-Version 3.x.y``. The output directory
may be changed from ``.``, and the package will be installed into a
subdirectory. By default, the subdirectory is named the same as the package,
and without the ``-ExcludeVersion`` option this name will include the specific
version installed. Inside the subdirectory is a ``tools`` directory that
contains the Python installation::
# Without -ExcludeVersion
> .\python.3.5.2\tools\python.exe -V
Python 3.5.2
# With -ExcludeVersion
> .\python\tools\python.exe -V
Python 3.5.2
In general, nuget packages are not upgradeable, and newer versions should be
installed side-by-side and referenced using the full path. Alternatively,
delete the package directory manually and install it again. Many CI systems
will do this automatically if they do not preserve files between builds.
Alongside the ``tools`` directory is a ``build\native`` directory. This
contains a MSBuild properties file ``python.props`` that can be used in a
C++ project to reference the Python install. Including the settings will
automatically use the headers and import libraries in your build.
The package information pages on nuget.org are
`www.nuget.org/packages/python <https://www.nuget.org/packages/python>`_
for the 64-bit version and `www.nuget.org/packages/pythonx86
<https://www.nuget.org/packages/pythonx86>`_ for the 32-bit version.
.. _windows-embeddable:
The embeddable package
======================
.. versionadded:: 3.5
The embedded distribution is a ZIP file containing a minimal Python environment.
It is intended for acting as part of another application, rather than being
directly accessed by end-users.
When extracted, the embedded distribution is (almost) fully isolated from the
user's system, including environment variables, system registry settings, and
installed packages. The standard library is included as pre-compiled and
optimized ``.pyc`` files in a ZIP, and ``python3.dll``, ``python37.dll``,
``python.exe`` and ``pythonw.exe`` are all provided. Tcl/tk (including all
dependants, such as Idle), pip and the Python documentation are not included.
.. note::
The embedded distribution does not include the `Microsoft C Runtime
<https://www.microsoft.com/en-us/download/details.aspx?id=48145>`_ and it is
the responsibility of the application installer to provide this. The
runtime may have already been installed on a user's system previously or
automatically via Windows Update, and can be detected by finding
``ucrtbase.dll`` in the system directory.
Third-party packages should be installed by the application installer alongside
the embedded distribution. Using pip to manage dependencies as for a regular
Python installation is not supported with this distribution, though with some
care it may be possible to include and use pip for automatic updates. In
general, third-party packages should be treated as part of the application
("vendoring") so that the developer can ensure compatibility with newer
versions before providing updates to users.
The two recommended use cases for this distribution are described below.
Python Application
------------------
An application written in Python does not necessarily require users to be aware
of that fact. The embedded distribution may be used in this case to include a
private version of Python in an install package. Depending on how transparent it
should be (or conversely, how professional it should appear), there are two
options.
Using a specialized executable as a launcher requires some coding, but provides
the most transparent experience for users. With a customized launcher, there are
no obvious indications that the program is running on Python: icons can be
customized, company and version information can be specified, and file
associations behave properly. In most cases, a custom launcher should simply be
able to call ``Py_Main`` with a hard-coded command line.
The simpler approach is to provide a batch file or generated shortcut that
directly calls the ``python.exe`` or ``pythonw.exe`` with the required
command-line arguments. In this case, the application will appear to be Python
and not its actual name, and users may have trouble distinguishing it from other
running Python processes or file associations.
With the latter approach, packages should be installed as directories alongside
the Python executable to ensure they are available on the path. With the
specialized launcher, packages can be located in other locations as there is an
opportunity to specify the search path before launching the application.
Embedding Python
----------------
Applications written in native code often require some form of scripting
language, and the embedded Python distribution can be used for this purpose. In
general, the majority of the application is in native code, and some part will
either invoke ``python.exe`` or directly use ``python3.dll``. For either case,
extracting the embedded distribution to a subdirectory of the application
installation is sufficient to provide a loadable Python interpreter.
As with the application use, packages can be installed to any location as there
is an opportunity to specify search paths before initializing the interpreter.
Otherwise, there is no fundamental differences between using the embedded
distribution and a regular installation.
Alternative bundles
@ -441,6 +620,8 @@ appropriate version of Python. It will prefer per-user installations over
system-wide ones, and orders by language version rather than using the most
recently installed version.
The launcher was originally specified in :pep:`397`.
Getting started
---------------
@ -922,95 +1103,19 @@ For extension modules, consult :ref:`building-on-windows`.
by Trent Apted et al, 2007
Embedded Distribution
=====================
.. versionadded:: 3.5
The embedded distribution is a ZIP file containing a minimal Python environment.
It is intended for acting as part of another application, rather than being
directly accessed by end-users.
When extracted, the embedded distribution is (almost) fully isolated from the
user's system, including environment variables, system registry settings, and
installed packages. The standard library is included as pre-compiled and
optimized ``.pyc`` files in a ZIP, and ``python3.dll``, ``python37.dll``,
``python.exe`` and ``pythonw.exe`` are all provided. Tcl/tk (including all
dependants, such as Idle), pip and the Python documentation are not included.
.. note::
The embedded distribution does not include the `Microsoft C Runtime
<https://www.microsoft.com/en-us/download/details.aspx?id=48145>`_ and it is
the responsibility of the application installer to provide this. The
runtime may have already been installed on a user's system previously or
automatically via Windows Update, and can be detected by finding
``ucrtbase.dll`` in the system directory.
Third-party packages should be installed by the application installer alongside
the embedded distribution. Using pip to manage dependencies as for a regular
Python installation is not supported with this distribution, though with some
care it may be possible to include and use pip for automatic updates. In
general, third-party packages should be treated as part of the application
("vendoring") so that the developer can ensure compatibility with newer
versions before providing updates to users.
The two recommended use cases for this distribution are described below.
Python Application
------------------
An application written in Python does not necessarily require users to be aware
of that fact. The embedded distribution may be used in this case to include a
private version of Python in an install package. Depending on how transparent it
should be (or conversely, how professional it should appear), there are two
options.
Using a specialized executable as a launcher requires some coding, but provides
the most transparent experience for users. With a customized launcher, there are
no obvious indications that the program is running on Python: icons can be
customized, company and version information can be specified, and file
associations behave properly. In most cases, a custom launcher should simply be
able to call ``Py_Main`` with a hard-coded command line.
The simpler approach is to provide a batch file or generated shortcut that
directly calls the ``python.exe`` or ``pythonw.exe`` with the required
command-line arguments. In this case, the application will appear to be Python
and not its actual name, and users may have trouble distinguishing it from other
running Python processes or file associations.
With the latter approach, packages should be installed as directories alongside
the Python executable to ensure they are available on the path. With the
specialized launcher, packages can be located in other locations as there is an
opportunity to specify the search path before launching the application.
Embedding Python
----------------
Applications written in native code often require some form of scripting
language, and the embedded Python distribution can be used for this purpose. In
general, the majority of the application is in native code, and some part will
either invoke ``python.exe`` or directly use ``python3.dll``. For either case,
extracting the embedded distribution to a subdirectory of the application
installation is sufficient to provide a loadable Python interpreter.
As with the application use, packages can be installed to any location as there
is an opportunity to specify search paths before initializing the interpreter.
Otherwise, there is no fundamental differences between using the embedded
distribution and a regular installation.
Other resources
Other Platforms
===============
.. seealso::
With ongoing development of Python, some platforms that used to be supported
earlier are no longer supported (due to the lack of users or developers).
Check :pep:`11` for details on all unsupported platforms.
`Python Programming On Win32 <http://shop.oreilly.com/product/9781565926219.do>`_
"Help for Windows Programmers"
by Mark Hammond and Andy Robinson, O'Reilly Media, 2000,
ISBN 1-56592-621-8
* `Windows CE <http://pythonce.sourceforge.net/>`_ is still supported.
* The `Cygwin <https://cygwin.com/>`_ installer offers to install the Python
interpreter as well (cf. `Cygwin package source
<ftp://ftp.uni-erlangen.de/pub/pc/gnuwin32/cygwin/mirrors/cygnus/
release/python>`_, `Maintainer releases
<http://www.tishler.net/jason/software/python/>`_)
`A Python for Windows Tutorial <http://www.imladris.com/Scripts/PythonForWindows.html>`_
by Amanda Birmingham, 2004
:pep:`397` - Python launcher for Windows
The proposal for the launcher to be included in the Python distribution.
See `Python for Windows <https://www.python.org/downloads/windows/>`_
for detailed information about platforms with pre-compiled installers.

View File

@ -1521,7 +1521,7 @@ class _BasePathTest(object):
# resolves to 'dirB/..' first before resolving to parent of dirB.
self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False)
# 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)
os.symlink(os.path.join(d), join('dirA', 'linkX'))
os.symlink(join('dirB'), os.path.join(d, 'linkY'))

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

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

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))

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

@ -0,0 +1,616 @@
"""
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
if ns.include_appxmanifest:
yield from in_build("python_uwp.exe", new_name="python")
yield from in_build("pythonw_uwp.exe", new_name="pythonw")
else:
yield from in_build("python.exe", new_name="python")
yield from in_build("pythonw.exe", new_name="pythonw")
yield from in_build(PYTHON_DLL_NAME)
if ns.include_launchers and ns.include_appxmanifest:
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>
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"
2 ICON DISCARDABLE "icons\py.ico"
3 ICON DISCARDABLE "icons\pyc.ico"
@ -14,6 +19,7 @@
5 ICON DISCARDABLE "icons\python.ico"
6 ICON DISCARDABLE "icons\pythonw.ico"
7 ICON DISCARDABLE "icons\setup.ico"
#endif
/////////////////////////////////////////////////////////////////////////////
//

239
PC/python_uwp.cpp Normal file
View File

@ -0,0 +1,239 @@
/* 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>
#ifdef PYTHON_UWP_SUPPORTED
#include <winrt\Windows.ApplicationModel.h>
#include <winrt\Windows.Storage.h>
#else
#include <string>
#endif
#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()
{
#ifdef PYTHON_UWP_SUPPORTED
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 (...) {
}
#endif
}
static const wchar_t *
get_argv0(const wchar_t *argv0)
{
#ifdef PYTHON_UWP_SUPPORTED
winrt::hstring installPath;
#else
std::wstring installPath;
#endif
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;
}
#ifdef PYTHON_UWP_SUPPORTED
try {
const auto package = winrt::Windows::ApplicationModel::Package::Current();
if (package) {
const auto install = package.InstalledLocation();
if (install) {
installPath = install.Path();
}
}
}
catch (...) {
}
#endif
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.7.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">
<Delete Files="@(_TclTkDLL->'$(OutDir)%(Filename)%(Extension)')" />
</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>

View File

@ -29,6 +29,16 @@
@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
@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.
@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 @(

View File

@ -9,6 +9,7 @@
<IncludeTests Condition="'$(IncludeTest)' == ''">true</IncludeTests>
<IncludeSSL Condition="'$(IncludeSSL)' == ''">true</IncludeSSL>
<IncludeTkinter Condition="'$(IncludeTkinter)' == ''">true</IncludeTkinter>
<IncludeUwp Condition="'$(IncludeUwp)' == ''">false</IncludeUwp>
</PropertyGroup>
<ItemDefinitionGroup>
@ -52,6 +53,8 @@
<ExtensionModules Include="_asyncio;_contextvars;_ctypes;_decimal;_elementtree;_msi;_multiprocessing;_overlapped;pyexpat;_queue;select;unicodedata;winsound" />
<!-- Extension modules that require external sources -->
<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 -->
<ExtensionModules Include="_socket" Condition="!$(IncludeSSL) or !$(IncludeExternals)" />
<ExternalModules Include="_ssl;_hashlib" Condition="$(IncludeSSL)" />
@ -70,6 +73,7 @@
<Projects2 Include="_freeze_importlib.vcxproj" />
<!-- python[w].exe -->
<Projects2 Include="python.vcxproj;pythonw.vcxproj" />
<Projects2 Include="python_uwp.vcxproj;pythonw_uwp.vcxproj" Condition="$(IncludeUwp)" />
<!-- venv[w]launcher.exe -->
<Projects2 Include="venvlauncher.vcxproj;venvwlauncher.vcxproj" />
</ItemGroup>

View File

@ -93,6 +93,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_queue", "_queue.vcxproj",
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "liblzma.vcxproj", "{12728250-16EC-4DC6-94D7-E21DD88947F8}"
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
GlobalSection(SolutionConfigurationPlatforms) = preSolution
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|x64.ActiveCfg = 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
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

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)">
<Warning Text="Not including zlib is not a supported configuration." />
</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>

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

@ -37,6 +37,7 @@ set BUILDX64=
set TARGET=Rebuild
set TESTTARGETDIR=
set PGO=-m test -q --pgo
set BUILDMSI=1
set BUILDNUGET=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-nuget" (set BUILDNUGET=) && 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
@ -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%
%MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true
if errorlevel 1 exit /B
%MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false
if errorlevel 1 exit /B
if defined BUILDMSI (
%MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true
if errorlevel 1 exit /B
%MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false
if errorlevel 1 exit /B
)
if defined BUILDZIP (
%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 --pgo Specify PGO command for x64 installers
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-zip Do not build embeddable package
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>
<TargetPath>$(OutputPath)\$(TargetName)$(TargetExt)</TargetPath>
<CleanCommand>rmdir /q/s "$(IntermediateOutputPath)\zip_$(ArchName)"</CleanCommand>
<Arguments>"$(PythonExe)" "$(MSBuildThisFileDirectory)\make_zip.py"</Arguments>
<Arguments>$(Arguments) -e -o "$(TargetPath)" -t "$(IntermediateOutputPath)\zip_$(ArchName)" -b "$(BuildPath.TrimEnd(`\`))"</Arguments>
<Environment>set DOC_FILENAME=python$(PythonVersion).chm</Environment>
<Arguments>"$(PythonExe)" "$(PySourcePath)PC\layout"</Arguments>
<Arguments>$(Arguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))"</Arguments>
<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 Condition="Exists($(CRTRedist))">$(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform)</Environment>
</PropertyGroup>
<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>
<TargetName>$(OutputName).$(NuspecVersion)</TargetName>
<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>$(PythonArguments) -t "$(IntermediateOutputPath)" -b "$(BuildPath.TrimEnd(`\`))"</PythonArguments>
<PythonArguments>"$(PythonExe)" "$(PySourcePath)PC\layout"</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)\python.exe" -B -m pip install -U $(Packages)</PackageArguments>
<PackageArguments Condition="$(Packages) != ''">"$(IntermediateOutputPath)pkg\pip.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>
<NugetArguments>$(NugetArguments) -OutputDirectory "$(OutputPath.Trim(`\`))"</NugetArguments>
<NugetArguments>$(NugetArguments) -Version "$(NuspecVersion)"</NugetArguments>
<NugetArguments>$(NugetArguments) -NoPackageAnalysis -NonInteractive</NugetArguments>
<Environment>set DOC_FILENAME=python$(PythonVersion).chm</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>
</PropertyGroup>
@ -48,22 +51,7 @@
<Target Name="_Build">
<Exec Command="$(CleanCommand)" />
<Exec Command="setlocal%0D%0A$(Environment)%0D%0A$(PythonArguments)" />
<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="setlocal%0D%0A$(Environment)%0D%0A$(PythonArguments)%0D%0A$(PackageArguments)" />
<Exec Command="$(NugetPackCommand) $(NugetArguments)" />
<Exec Command="$(NugetPackSymbolsCommand) $(NugetArguments)" Condition="$(NugetPackSymbolsCommand) != ''" />