diff --git a/Tools/msi/README.txt b/Tools/msi/README.txt index 25dcf32c2c4..7023b611819 100644 --- a/Tools/msi/README.txt +++ b/Tools/msi/README.txt @@ -62,10 +62,14 @@ the initial download size by separating them into their own MSIs. 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. @@ -80,8 +84,13 @@ available, it will simply be excluded from the installer. Ensure 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 @@ -175,6 +184,38 @@ The following properties may be passed when building these projects. 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 --user [--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 for other purposes. This path should generally not include +any version specifier, as that will be added automatically. + Modifying the Installer ======================= @@ -298,9 +339,9 @@ based on whether the install is for all users of the machine or just for 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. @@ -311,6 +352,8 @@ interpreter. Only the current user can access this location. This 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 @@ -487,6 +530,6 @@ Removing Python will clean up all the files and registry keys that were 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. diff --git a/Tools/msi/build.bat b/Tools/msi/build.bat index 5f53a1be6cb..b11579b116b 100644 --- a/Tools/msi/build.bat +++ b/Tools/msi/build.bat @@ -19,6 +19,8 @@ if "%~1" EQU "--pack" (set BUILDPACK=1) && shift && goto CheckOpts if not defined BUILDX86 if not defined BUILDX64 (set BUILDX86=1) && (set BUILDX64=1) +call "%D%get_externals.bat" + call "%PCBUILD%env.bat" x86 if defined BUILDX86 ( @@ -48,12 +50,10 @@ if defined BUILDPACK ( ) if defined BUILDX86 ( - "%PCBUILD%win32\python.exe" "%D%get_wix.py" msbuild %BUILD_CMD% if errorlevel 1 goto :eof ) if defined BUILDX64 ( - "%PCBUILD%amd64\python.exe" "%D%get_wix.py" msbuild /p:Platform=x64 %BUILD_CMD% if errorlevel 1 goto :eof ) diff --git a/Tools/msi/buildrelease.bat b/Tools/msi/buildrelease.bat index ca08df84032..fc7cb9fdc1f 100644 --- a/Tools/msi/buildrelease.bat +++ b/Tools/msi/buildrelease.bat @@ -29,6 +29,7 @@ set DOWNLOAD_URL=https://www.python.org/ftp/python/{version}/{arch}{releasename} set D=%~dp0 set PCBUILD=%D%..\..\PCBuild\ +set EXTERNALS=%D%..\..\externals\windows-installer\ set BUILDX86= set BUILDX64= @@ -59,12 +60,15 @@ if "%1" NEQ "" echo Invalid option: "%1" && exit /B 1 if not defined BUILDX86 if not defined BUILDX64 (set BUILDX86=1) && (set BUILDX64=1) +call "%D%get_externals.bat" + :builddoc if "%SKIPBUILD%" EQU "1" goto skipdoc if "%SKIPDOC%" EQU "1" goto skipdoc if not defined PYTHON where py -q || echo Cannot find py on path and PYTHON is not set. && exit /B 1 if not defined SPHINXBUILD where sphinx-build -q || echo Cannot find sphinx-build on path and SPHINXBUILD is not set. && exit /B 1 + call "%D%..\..\doc\make.bat" htmlhelp if errorlevel 1 goto :eof :skipdoc @@ -73,7 +77,7 @@ where hg /q || echo Cannot find Mercurial on PATH && exit /B 1 where dlltool /q && goto skipdlltoolsearch set _DLLTOOL_PATH= -where /R "%D%..\..\externals" dlltool > "%TEMP%\dlltool.loc" 2> nul && set /P _DLLTOOL_PATH= < "%TEMP%\dlltool.loc" & del "%TEMP%\dlltool.loc" +where /R "%EXTERNALS%\" dlltool > "%TEMP%\dlltool.loc" 2> nul && set /P _DLLTOOL_PATH= < "%TEMP%\dlltool.loc" & del "%TEMP%\dlltool.loc" if not exist "%_DLLTOOL_PATH%" echo Cannot find binutils on PATH or in external && exit /B 1 for %%f in (%_DLLTOOL_PATH%) do set PATH=%PATH%;%%~dpf set _DLLTOOL_PATH= @@ -170,8 +174,6 @@ if not "%SKIPBUILD%" EQU "1" ( @echo off ) -"%BUILD%python.exe" "%D%get_wix.py" - set BUILDOPTS=/p:Platform=%1 /p:BuildForRelease=true /p:DownloadUrl=%DOWNLOAD_URL% /p:DownloadUrlBase=%DOWNLOAD_URL_BASE% /p:ReleaseUri=%RELEASE_URI% if "%PGO%" NEQ "" set BUILDOPTS=%BUILDOPTS% /p:PGOBuildPath=%BUILD% msbuild "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true diff --git a/Tools/msi/get_externals.bat b/Tools/msi/get_externals.bat new file mode 100644 index 00000000000..4ead75e7572 --- /dev/null +++ b/Tools/msi/get_externals.bat @@ -0,0 +1,27 @@ +@echo off +setlocal +rem Simple script to fetch source for external tools + +where /Q svn +if ERRORLEVEL 1 ( + echo.svn.exe must be on your PATH to get external tools. + echo.Try TortoiseSVN (http://tortoisesvn.net/^) and be sure to check the + echo.command line tools option. + popd + exit /b 1 +) + +if not exist "%~dp0..\..\externals" mkdir "%~dp0..\..\externals" +pushd "%~dp0..\..\externals" + +if "%SVNROOT%"=="" set SVNROOT=http://svn.python.org/projects/external/ + +if not exist "windows-installer\.svn" ( + echo.Checking out installer dependencies to %CD%\windows-installer + svn co %SVNROOT%windows-installer +) else ( + echo.Updating installer dependencies in %CD%\windows-installer + svn up windows-installer +) + +popd diff --git a/Tools/msi/get_wix.py b/Tools/msi/get_wix.py deleted file mode 100644 index db141567cbd..00000000000 --- a/Tools/msi/get_wix.py +++ /dev/null @@ -1,49 +0,0 @@ -''' -Downloads and extracts WiX to a local directory -''' - -__author__ = 'Steve Dower ' - -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') diff --git a/Tools/msi/msi.props b/Tools/msi/msi.props index 1e2d3e28f57..01a81b4a96f 100644 --- a/Tools/msi/msi.props +++ b/Tools/msi/msi.props @@ -53,7 +53,7 @@ $(OutputPath)\ $(OutputPath) true - $(ExternalsDir)\redist + $(ExternalsDir)\windows-installer\redist python$(MajorVersionNumber)$(MinorVersionNumber)$(MicroVersionNumber)$(ReleaseLevelName).chm diff --git a/Tools/msi/uploadrelease.bat b/Tools/msi/uploadrelease.bat index 796763730a6..4e319ce4c98 100644 --- a/Tools/msi/uploadrelease.bat +++ b/Tools/msi/uploadrelease.bat @@ -8,6 +8,7 @@ set HOST= set USER= set TARGET= set DRYRUN=false +set NOGPG= :CheckOpts if "%1" EQU "-h" goto Help @@ -18,6 +19,7 @@ if "%1" EQU "--user" (set USER=%~2) && shift && shift && goto CheckOpts if "%1" EQU "-t" (set TARGET=%~2) && shift && shift && goto CheckOpts if "%1" EQU "--target" (set TARGET=%~2) && shift && shift && goto CheckOpts if "%1" EQU "--dry-run" (set DRYRUN=true) && shift && goto CheckOpts +if "%1" EQU "--no-gpg" (set NOGPG=true) && shift && goto CheckOpts if not defined PLINK where plink > "%TEMP%\plink.loc" 2> nul && set /P PLINK= < "%TEMP%\plink.loc" & del "%TEMP%\plink.loc" 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" @@ -31,10 +33,15 @@ if not defined PSCP where /R "%ProgramFiles(x86)%" pscp > "%TEMP%\pscp.loc" 2> n if not defined PSCP echo Cannot locate pscp.exe & exit /B 1 echo Found pscp.exe at %PSCP% -if not defined GPG where gpg2 > "%TEMP%\gpg.loc" 2> nul && set /P GPG= < "%TEMP%\gpg.loc" & del "%TEMP%\gpg.loc" -if not defined GPG where /R "%PCBUILD%..\externals" gpg2 > "%TEMP%\gpg.loc" 2> nul && set /P GPG= < "%TEMP%\gpg.loc" & del "%TEMP%\gpg.loc" -if not defined GPG echo Cannot locate gpg2.exe. Signatures will not be uploaded & pause -echo Found gpg2.exe at %GPG% +if defined NOGPG ( + set GPG= + echo Skipping GPG signature generation because of --no-gpg +) else ( + if not defined GPG where gpg2 > "%TEMP%\gpg.loc" 2> nul && set /P GPG= < "%TEMP%\gpg.loc" & del "%TEMP%\gpg.loc" + 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" + if not defined GPG echo Cannot locate gpg2.exe. Signatures will not be uploaded & pause + echo Found gpg2.exe at %GPG% +) call "%PCBUILD%env.bat" > nul 2> nul pushd "%D%" diff --git a/Tools/msi/wix.props b/Tools/msi/wix.props index 35492f9b8dd..fbb2d1020ec 100644 --- a/Tools/msi/wix.props +++ b/Tools/msi/wix.props @@ -4,9 +4,9 @@ $(MSBuildThisFileDirectory)\Wix\ - $(ExternalsDir)\Wix\ - $(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Installer XML\3.9@InstallRoot) - $(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows Installer XML\3.9@InstallRoot) + $(ExternalsDir)\windows-installer\wix\ + $(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Installer XML\3.10@InstallRoot) + $(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows Installer XML\3.10@InstallRoot) $(WixInstallPath)\Wix.targets \ No newline at end of file