Building the Installer
======================
+Before building the installer, download extra build dependencies using
+Tools\msi\get_externals.bat. (Note that this is in addition to the
+similarly named file in PCBuild.)
+
For testing, the installer should be built with the Tools/msi/build.bat
script:
- build.bat [-x86] [-x64] [--doc]
+ build.bat [-x86] [-x64] [--doc] [--test-marker] [--pack]
This script will build the required configurations of Python and
generate an installer layout in PCBuild/(win32|amd64)/en-us.
also set %HTMLHELP% to the Html Help Compiler (hhc.exe), or put HHC on
your PATH or in externals/.
-If WiX is not found on your system, it will be automatically downloaded
-and extracted to the externals/ directory.
+Specify --test-marker to build an installer that works side-by-side with
+an official Python release. All registry keys and install locations will
+include an extra marker to avoid overwriting files. This marker is
+currently an 'x' prefix, but may change at any time.
+
+Specify --pack to build an installer that does not require all MSIs to
+be available alongside. This takes longer, but is easier to share.
For an official release, the installer should be built with the
When true, rebuilds all of the MSIs making up the layout. Defaults to
true.
+Uploading the Installer
+=======================
+
+For official releases, the uploadrelease.bat script should be used.
+
+You will require PuTTY so that plink.exe and pscp.exe can be used, and your
+SSH key can be activated in pageant.exe. PuTTY should be either on your path
+or in %ProgramFiles(x86)%\PuTTY.
+
+To include signatures for each uploaded file, you will need gpg2.exe on your
+path or have run get_externals.bat. You may also need to "gpg2.exe --import"
+your key before running the upload script.
+
+ uploadrelease.bat --host <host> --user <username> [--dry-run] [--no-gpg]
+
+The host is the URL to the server. This can be provided by the Release
+Manager. You should be able to SSH to this address.
+
+The username is your own username, which you have permission to SSH into
+the server containing downloads.
+
+Use --dry-run to display the generated upload commands without executing
+them. Signatures for each file will be generated but not uploaded unless
+--no-gpg is also passed.
+
+Use --no-gpg to suppress signature generation and upload.
+
+The default target directory (which appears in uploadrelease.proj) is
+correct for official Python releases, but may be overridden with
+--target <path> for other purposes. This path should generally not include
+any version specifier, as that will be added automatically.
+
Modifying the Installer
=======================
the user performing the installation.
The default installation location when installing for all users is
-"%ProgramFiles%\Python 3.X" for the 64-bit interpreter and
-"%ProgramFiles(x86)%\Python 3.X" for the 32-bit interpreter. (Note that
-the latter path is equivalent to "%ProgramFiles%\Python 3.X" when
+"%ProgramFiles%\Python3X" for the 64-bit interpreter and
+"%ProgramFiles(x86)%\Python3X-32" for the 32-bit interpreter. (Note that
+the latter path is equivalent to "%ProgramFiles%\Python3X-32" when
running a 32-bit version of Windows.) This location requires
administrative privileges to install or later modify the installation.
provides a suitable level of protection against malicious modification
of Python's files.
+(Default installation locations are set in Tools\msi\bundle\bundle.wxs.)
+
Within this install directory is the following approximate layout:
.\python[w].exe The core executable files
created by the installer, as well as __pycache__ folders that are
explicitly handled by the installer. Python packages installed later
using a tool like pip will not be removed. Some components may be
-installed by other installers (such as the MSVCRT) and these will not be
-removed if another product has a dependency on them.
+installed by other installers and these will not be removed if another
+product has a dependency on them.
\r
if not defined BUILDX86 if not defined BUILDX64 (set BUILDX86=1) && (set BUILDX64=1)\r
\r
+call "%D%get_externals.bat"\r
+\r
call "%PCBUILD%env.bat" x86\r
\r
if defined BUILDX86 (\r
)\r
\r
if defined BUILDX86 (\r
- "%PCBUILD%win32\python.exe" "%D%get_wix.py"\r
msbuild %BUILD_CMD%\r
if errorlevel 1 goto :eof\r
)\r
if defined BUILDX64 (\r
- "%PCBUILD%amd64\python.exe" "%D%get_wix.py"\r
msbuild /p:Platform=x64 %BUILD_CMD%\r
if errorlevel 1 goto :eof\r
)\r
\r
set D=%~dp0\r
set PCBUILD=%D%..\..\PCBuild\\r
+set EXTERNALS=%D%..\..\externals\windows-installer\\r
\r
set BUILDX86=\r
set BUILDX64=\r
\r
if not defined BUILDX86 if not defined BUILDX64 (set BUILDX86=1) && (set BUILDX64=1)\r
\r
+call "%D%get_externals.bat"\r
+\r
:builddoc\r
if "%SKIPBUILD%" EQU "1" goto skipdoc\r
if "%SKIPDOC%" EQU "1" goto skipdoc\r
\r
if not defined PYTHON where py -q || echo Cannot find py on path and PYTHON is not set. && exit /B 1\r
if not defined SPHINXBUILD where sphinx-build -q || echo Cannot find sphinx-build on path and SPHINXBUILD is not set. && exit /B 1\r
+\r
call "%D%..\..\doc\make.bat" htmlhelp\r
if errorlevel 1 goto :eof\r
:skipdoc\r
\r
where dlltool /q && goto skipdlltoolsearch\r
set _DLLTOOL_PATH=\r
-where /R "%D%..\..\externals" dlltool > "%TEMP%\dlltool.loc" 2> nul && set /P _DLLTOOL_PATH= < "%TEMP%\dlltool.loc" & del "%TEMP%\dlltool.loc" \r
+where /R "%EXTERNALS%\" dlltool > "%TEMP%\dlltool.loc" 2> nul && set /P _DLLTOOL_PATH= < "%TEMP%\dlltool.loc" & del "%TEMP%\dlltool.loc" \r
if not exist "%_DLLTOOL_PATH%" echo Cannot find binutils on PATH or in external && exit /B 1\r
for %%f in (%_DLLTOOL_PATH%) do set PATH=%PATH%;%%~dpf\r
set _DLLTOOL_PATH=\r
@echo off\r
)\r
\r
-"%BUILD%python.exe" "%D%get_wix.py"\r
-\r
set BUILDOPTS=/p:Platform=%1 /p:BuildForRelease=true /p:DownloadUrl=%DOWNLOAD_URL% /p:DownloadUrlBase=%DOWNLOAD_URL_BASE% /p:ReleaseUri=%RELEASE_URI%\r
if "%PGO%" NEQ "" set BUILDOPTS=%BUILDOPTS% /p:PGOBuildPath=%BUILD%\r
msbuild "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true\r
--- /dev/null
+@echo off\r
+setlocal\r
+rem Simple script to fetch source for external tools\r
+\r
+where /Q svn\r
+if ERRORLEVEL 1 (\r
+ echo.svn.exe must be on your PATH to get external tools.\r
+ echo.Try TortoiseSVN (http://tortoisesvn.net/^) and be sure to check the\r
+ echo.command line tools option.\r
+ popd\r
+ exit /b 1\r
+)\r
+\r
+if not exist "%~dp0..\..\externals" mkdir "%~dp0..\..\externals"\r
+pushd "%~dp0..\..\externals"\r
+\r
+if "%SVNROOT%"=="" set SVNROOT=http://svn.python.org/projects/external/\r
+\r
+if not exist "windows-installer\.svn" (\r
+ echo.Checking out installer dependencies to %CD%\windows-installer\r
+ svn co %SVNROOT%windows-installer\r
+) else (\r
+ echo.Updating installer dependencies in %CD%\windows-installer\r
+ svn up windows-installer\r
+)\r
+\r
+popd\r
+++ /dev/null
-'''
-Downloads and extracts WiX to a local directory
-'''
-
-__author__ = 'Steve Dower <steve.dower@microsoft.com>'
-
-import io
-import os
-import sys
-
-from pathlib import Path
-from subprocess import Popen
-from zipfile import ZipFile
-
-EXTERNALS_DIR = None
-for p in (Path.cwd() / __file__).parents:
- if any(p.glob("PCBuild/*.vcxproj")):
- EXTERNALS_DIR = p / "externals"
- break
-
-if not EXTERNALS_DIR:
- print("Cannot find project root")
- sys.exit(1)
-
-WIX_BINARIES_ZIP = 'http://wixtoolset.org/downloads/v3.10.0.1823/wix310-binaries.zip'
-TARGET_BIN_ZIP = EXTERNALS_DIR / "wix.zip"
-TARGET_BIN_DIR = EXTERNALS_DIR / "wix"
-
-POWERSHELL_COMMAND = "[IO.File]::WriteAllBytes('{}', (Invoke-WebRequest {} -UseBasicParsing).Content)"
-
-if __name__ == '__main__':
- if TARGET_BIN_DIR.exists() and any(TARGET_BIN_DIR.glob("*")):
- print('WiX is already installed')
- sys.exit(0)
-
- try:
- TARGET_BIN_DIR.mkdir()
- except FileExistsError:
- pass
-
- print('Downloading WiX to', TARGET_BIN_ZIP)
- p = Popen(["powershell.exe", "-Command", POWERSHELL_COMMAND.format(TARGET_BIN_ZIP, WIX_BINARIES_ZIP)])
- p.wait()
- print('Extracting WiX to', TARGET_BIN_DIR)
- with ZipFile(str(TARGET_BIN_ZIP)) as z:
- z.extractall(str(TARGET_BIN_DIR))
- TARGET_BIN_ZIP.unlink()
-
- print('Extracted WiX')
<OutputPath Condition="!HasTrailingSlash($(OutputPath))">$(OutputPath)\</OutputPath>
<OutDir>$(OutputPath)</OutDir>
<ReuseCabinetCache>true</ReuseCabinetCache>
- <CRTRedist Condition="'$(CRTRedist)' == ''">$(ExternalsDir)\redist</CRTRedist>
+ <CRTRedist Condition="'$(CRTRedist)' == ''">$(ExternalsDir)\windows-installer\redist</CRTRedist>
<CRTRedist Condition="!Exists($(CRTRedist))"></CRTRedist>
<DocFilename>python$(MajorVersionNumber)$(MinorVersionNumber)$(MicroVersionNumber)$(ReleaseLevelName).chm</DocFilename>
set USER=\r
set TARGET=\r
set DRYRUN=false\r
+set NOGPG=\r
\r
:CheckOpts\r
if "%1" EQU "-h" goto Help\r
if "%1" EQU "-t" (set TARGET=%~2) && shift && shift && goto CheckOpts\r
if "%1" EQU "--target" (set TARGET=%~2) && shift && shift && goto CheckOpts\r
if "%1" EQU "--dry-run" (set DRYRUN=true) && shift && goto CheckOpts\r
+if "%1" EQU "--no-gpg" (set NOGPG=true) && shift && goto CheckOpts\r
\r
if not defined PLINK where plink > "%TEMP%\plink.loc" 2> nul && set /P PLINK= < "%TEMP%\plink.loc" & del "%TEMP%\plink.loc"\r
if not defined PLINK where /R "%ProgramFiles(x86)%\PuTTY" plink > "%TEMP%\plink.loc" 2> nul && set /P PLINK= < "%TEMP%\plink.loc" & del "%TEMP%\plink.loc"\r
if not defined PSCP echo Cannot locate pscp.exe & exit /B 1\r
echo Found pscp.exe at %PSCP%\r
\r
-if not defined GPG where gpg2 > "%TEMP%\gpg.loc" 2> nul && set /P GPG= < "%TEMP%\gpg.loc" & del "%TEMP%\gpg.loc"\r
-if not defined GPG where /R "%PCBUILD%..\externals" gpg2 > "%TEMP%\gpg.loc" 2> nul && set /P GPG= < "%TEMP%\gpg.loc" & del "%TEMP%\gpg.loc"\r
-if not defined GPG echo Cannot locate gpg2.exe. Signatures will not be uploaded & pause\r
-echo Found gpg2.exe at %GPG%\r
+if defined NOGPG (\r
+ set GPG=\r
+ echo Skipping GPG signature generation because of --no-gpg\r
+) else (\r
+ if not defined GPG where gpg2 > "%TEMP%\gpg.loc" 2> nul && set /P GPG= < "%TEMP%\gpg.loc" & del "%TEMP%\gpg.loc"\r
+ if not defined GPG where /R "%PCBUILD%..\externals\windows-installer" gpg2 > "%TEMP%\gpg.loc" 2> nul && set /P GPG= < "%TEMP%\gpg.loc" & del "%TEMP%\gpg.loc"\r
+ if not defined GPG echo Cannot locate gpg2.exe. Signatures will not be uploaded & pause\r
+ echo Found gpg2.exe at %GPG%\r
+)\r
\r
call "%PCBUILD%env.bat" > nul 2> nul\r
pushd "%D%"\r
<PropertyGroup>
<WixInstallPath Condition="'$(WixInstallPath)' == '' and Exists('$(MSBuildThisFileDirectory)\Wix')">$(MSBuildThisFileDirectory)\Wix\</WixInstallPath>
- <WixInstallPath Condition="'$(WixInstallPath)' == '' and Exists('$(ExternalsDir)\Wix')">$(ExternalsDir)\Wix\</WixInstallPath>
- <WixInstallPath Condition="'$(WixInstallPath)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Installer XML\3.9@InstallRoot)</WixInstallPath>
- <WixInstallPath Condition="'$(WixInstallPath)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows Installer XML\3.9@InstallRoot)</WixInstallPath>
+ <WixInstallPath Condition="'$(WixInstallPath)' == '' and Exists('$(ExternalsDir)\windows-installer\wix')">$(ExternalsDir)\windows-installer\wix\</WixInstallPath>
+ <WixInstallPath Condition="'$(WixInstallPath)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Installer XML\3.10@InstallRoot)</WixInstallPath>
+ <WixInstallPath Condition="'$(WixInstallPath)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows Installer XML\3.10@InstallRoot)</WixInstallPath>
<WixTargetsPath>$(WixInstallPath)\Wix.targets</WixTargetsPath>
</PropertyGroup>
</Project>
\ No newline at end of file