2004-08-22 10:34:34 -03:00
# Python MSI Generator
# (C) 2003 Martin v. Loewis
# See "FOO" in comments refers to MSDN sections with the title FOO.
2010-11-14 14:17:39 -04:00
import msilib , schema , sequence , os , glob , time , re , shutil , zipfile
2004-08-22 10:34:34 -03:00
from msilib import Feature , CAB , Directory , Dialog , Binary , add_data
import uisample
from win32com . client import constants
2004-12-22 09:41:49 -04:00
from distutils . spawn import find_executable
2006-03-28 14:30:05 -04:00
from uuids import product_codes
2010-03-16 15:49:28 -03:00
import tempfile
2004-08-22 10:34:34 -03:00
# Settings can be overridden in config.py below
# 0 for official python.org releases
# 1 for intermediate releases by anybody, with
# a new product code for every package.
snapshot = 1
# 1 means that file extension is px, not py,
# and binaries start with x
testpackage = 0
# Location of build tree
srcdir = os . path . abspath ( " ../.. " )
# Text to be displayed as the version in dialogs etc.
# goes into file name and ProductCode. Defaults to
# current_version.day for Snapshot, current_version otherwise
full_current_version = None
2004-09-01 11:51:06 -03:00
# Is Tcl available at all?
have_tcl = True
2007-12-04 10:57:30 -04:00
# path to PCbuild directory
2008-01-06 07:03:43 -04:00
PCBUILD = " PCbuild "
2007-12-04 10:57:30 -04:00
# msvcrt version
2008-01-06 07:03:43 -04:00
MSVCR = " 90 "
2010-03-16 15:49:28 -03:00
# Name of certificate in default store to sign MSI with
certname = None
2010-11-14 14:17:39 -04:00
# Make a zip file containing the PDB files for this build?
pdbzip = True
2004-08-22 10:34:34 -03:00
try :
from config import *
except ImportError :
pass
# Extract current version from Include/patchlevel.h
lines = open ( srcdir + " /Include/patchlevel.h " ) . readlines ( )
major = minor = micro = level = serial = None
levels = {
' PY_RELEASE_LEVEL_ALPHA ' : 0xA ,
' PY_RELEASE_LEVEL_BETA ' : 0xB ,
' PY_RELEASE_LEVEL_GAMMA ' : 0xC ,
' PY_RELEASE_LEVEL_FINAL ' : 0xF
}
for l in lines :
if not l . startswith ( " #define " ) :
continue
l = l . split ( )
if len ( l ) != 3 :
continue
_ , name , value = l
if name == ' PY_MAJOR_VERSION ' : major = value
if name == ' PY_MINOR_VERSION ' : minor = value
if name == ' PY_MICRO_VERSION ' : micro = value
if name == ' PY_RELEASE_LEVEL ' : level = levels [ value ]
if name == ' PY_RELEASE_SERIAL ' : serial = value
short_version = major + " . " + minor
# See PC/make_versioninfo.c
FIELD3 = 1000 * int ( micro ) + 10 * level + int ( serial )
current_version = " %s . %d " % ( short_version , FIELD3 )
# This should never change. The UpgradeCode of this package can be
# used in the Upgrade table of future packages to make the future
# package replace this one. See "UpgradeCode Property".
2008-09-13 05:11:57 -03:00
# upgrade_code gets set to upgrade_code_64 when we have determined
# that the target is Win64.
2004-08-22 10:34:34 -03:00
upgrade_code_snapshot = ' { 92A24481-3ECB-40FC-8836-04B7966EC0D5} '
upgrade_code = ' { 65E6DE48-A358-434D-AA4F-4AF72DB4718F} '
2008-09-13 05:11:57 -03:00
upgrade_code_64 = ' { 6A965A0C-6EE6-4E3A-9983-3263F56311EC} '
2004-08-22 10:34:34 -03:00
if snapshot :
current_version = " %s . %s . %s " % ( major , minor , int ( time . time ( ) / 3600 / 24 ) )
product_code = msilib . gen_uuid ( )
else :
product_code = product_codes [ current_version ]
if full_current_version is None :
full_current_version = current_version
extensions = [
' bz2.pyd ' ,
' pyexpat.pyd ' ,
' select.pyd ' ,
' unicodedata.pyd ' ,
' winsound.pyd ' ,
2005-12-15 18:08:46 -04:00
' _elementtree.pyd ' ,
2004-08-22 10:34:34 -03:00
' _bsddb.pyd ' ,
' _socket.pyd ' ,
' _ssl.pyd ' ,
' _testcapi.pyd ' ,
' _tkinter.pyd ' ,
2006-03-05 10:04:26 -04:00
' _msi.pyd ' ,
2006-03-10 11:36:28 -04:00
' _ctypes.pyd ' ,
2006-04-04 04:10:59 -03:00
' _ctypes_test.pyd ' ,
2006-08-16 09:55:10 -03:00
' _sqlite3.pyd ' ,
2008-06-30 04:01:09 -03:00
' _hashlib.pyd ' ,
' _multiprocessing.pyd '
2004-08-22 10:34:34 -03:00
]
2005-03-14 20:39:40 -04:00
# Well-known component UUIDs
# These are needed for SharedDLLs reference counter; if
# a different UUID was used for each incarnation of, say,
# python24.dll, an upgrade would set the reference counter
# from 1 to 2 (due to what I consider a bug in MSI)
# Using the same UUID is fine since these files are versioned,
# so Installer will always keep the newest version.
2007-12-04 10:57:30 -04:00
# NOTE: All uuids are self generated.
2005-03-14 20:39:40 -04:00
pythondll_uuid = {
" 24 " : " { 9B81E618-2301-4035-AC77-75D9ABEB7301} " ,
2007-08-30 15:15:22 -03:00
" 25 " : " { 2e41b118-38bd-4c1b-a840-6977efd1b911} " ,
2007-08-14 02:01:50 -03:00
" 26 " : " { 34ebecac-f046-4e1c-b0e3-9bac3cdaacfa} " ,
2008-10-02 17:04:47 -03:00
" 27 " : " { 4fe21c76-1760-437b-a2f2-99909130a175} " ,
2005-03-14 20:39:40 -04:00
} [ major + minor ]
2005-03-27 21:08:02 -04:00
2008-05-09 15:21:55 -03:00
# Compute the name that Sphinx gives to the docfile
docfile = " "
2009-06-28 09:24:23 -03:00
if int ( micro ) :
docfile = micro
2008-05-09 15:21:55 -03:00
if level < 0xf :
2009-06-13 12:48:04 -03:00
if level == 0xC :
2009-06-28 09:24:23 -03:00
docfile + = " rc %s " % ( serial , )
2009-06-13 12:48:04 -03:00
else :
2009-06-28 09:24:23 -03:00
docfile + = ' %x %s ' % ( level , serial )
2008-05-09 15:21:55 -03:00
docfile = ' python %s %s %s .chm ' % ( major , minor , docfile )
2004-12-22 09:41:49 -04:00
# Build the mingw import library, libpythonXY.a
# This requires 'nm' and 'dlltool' executables on your PATH
def build_mingw_lib ( lib_file , def_file , dll_file , mingw_lib ) :
warning = " WARNING: %s - libpythonXX.a not built "
nm = find_executable ( ' nm ' )
dlltool = find_executable ( ' dlltool ' )
if not nm or not dlltool :
print warning % " nm and/or dlltool were not found "
return False
nm_command = ' %s -Cs %s ' % ( nm , lib_file )
dlltool_command = " %s --dllname %s --def %s --output-lib %s " % \
( dlltool , dll_file , def_file , mingw_lib )
export_match = re . compile ( r " ^_imp__(.*) in python \ d+ \ .dll " ) . match
f = open ( def_file , ' w ' )
print >> f , " LIBRARY %s " % dll_file
print >> f , " EXPORTS "
nm_pipe = os . popen ( nm_command )
for line in nm_pipe . readlines ( ) :
m = export_match ( line )
if m :
print >> f , m . group ( 1 )
f . close ( )
exit = nm_pipe . close ( )
if exit :
print warning % " nm did not run successfully "
return False
if os . system ( dlltool_command ) != 0 :
print warning % " dlltool did not run successfully "
return False
return True
# Target files (.def and .a) go in PCBuild directory
2007-12-04 10:57:30 -04:00
lib_file = os . path . join ( srcdir , PCBUILD , " python %s %s .lib " % ( major , minor ) )
def_file = os . path . join ( srcdir , PCBUILD , " python %s %s .def " % ( major , minor ) )
2004-12-22 09:41:49 -04:00
dll_file = " python %s %s .dll " % ( major , minor )
2007-12-04 10:57:30 -04:00
mingw_lib = os . path . join ( srcdir , PCBUILD , " libpython %s %s .a " % ( major , minor ) )
2004-12-22 09:41:49 -04:00
have_mingw = build_mingw_lib ( lib_file , def_file , dll_file , mingw_lib )
2011-03-15 13:55:01 -03:00
# Determine the target architecture
2012-02-21 13:12:02 -04:00
if os . system ( " nmake /nologo /c /f msisupport.mak " ) != 0 :
raise RuntimeError ( " ' nmake /f msisupport.mak ' failed " )
2007-12-04 10:57:30 -04:00
dll_path = os . path . join ( srcdir , PCBUILD , dll_file )
2006-02-14 16:42:55 -04:00
msilib . set_arch_from_file ( dll_path )
if msilib . pe_type ( dll_path ) != msilib . pe_type ( " msisupport.dll " ) :
raise SystemError , " msisupport.dll for incorrect architecture "
2008-09-13 05:11:57 -03:00
if msilib . Win64 :
upgrade_code = upgrade_code_64
2008-09-13 05:36:22 -03:00
# Bump the last digit of the code by one, so that 32-bit and 64-bit
# releases get separate product codes
digit = hex ( ( int ( product_code [ - 2 ] , 16 ) + 1 ) % 16 ) [ - 1 ]
product_code = product_code [ : - 2 ] + digit + ' } '
2006-02-14 16:42:55 -04:00
2004-08-22 10:34:34 -03:00
if testpackage :
ext = ' px '
testprefix = ' x '
else :
ext = ' py '
testprefix = ' '
if msilib . Win64 :
2007-08-30 15:25:47 -03:00
SystemFolderName = " [System64Folder] "
2007-08-31 07:01:07 -03:00
registry_component = 4 | 256
2004-08-22 10:34:34 -03:00
else :
SystemFolderName = " [SystemFolder] "
2007-08-31 07:01:07 -03:00
registry_component = 4
2004-08-22 10:34:34 -03:00
msilib . reset ( )
# condition in which to install pythonxy.dll in system32:
# a) it is Windows 9x or
# b) it is NT, the user is privileged, and has chosen per-machine installation
sys32cond = " (Windows9x or (Privileged and ALLUSERS)) "
def build_database ( ) :
""" Generate an empty database, with just the schema and the
Summary information stream . """
if snapshot :
uc = upgrade_code_snapshot
else :
uc = upgrade_code
2008-09-13 05:36:22 -03:00
if msilib . Win64 :
productsuffix = " (64-bit) "
else :
productsuffix = " "
2004-08-22 10:34:34 -03:00
# schema represents the installer 2.0 database schema.
# sequence is the set of standard sequences
# (ui/execute, admin/advt/install)
2010-03-16 15:49:28 -03:00
msiname = " python- %s %s .msi " % ( full_current_version , msilib . arch_ext )
db = msilib . init_database ( msiname ,
2008-09-13 05:36:22 -03:00
schema , ProductName = " Python " + full_current_version + productsuffix ,
2004-08-22 10:34:34 -03:00
ProductCode = product_code ,
ProductVersion = current_version ,
2008-10-13 08:23:35 -03:00
Manufacturer = u " Python Software Foundation " ,
request_uac = True )
2004-08-22 10:34:34 -03:00
# The default sequencing of the RemoveExistingProducts action causes
# removal of files that got just installed. Place it after
# InstallInitialize, so we first uninstall everything, but still roll
# back in case the installation is interrupted
msilib . change_sequence ( sequence . InstallExecuteSequence ,
" RemoveExistingProducts " , 1510 )
msilib . add_tables ( db , sequence )
# We cannot set ALLUSERS in the property table, as this cannot be
# reset if the user choses a per-user installation. Instead, we
# maintain WhichUsers, which can be "ALL" or "JUSTME". The UI manages
# this property, and when the execution starts, ALLUSERS is set
# accordingly.
add_data ( db , " Property " , [ ( " UpgradeCode " , uc ) ,
( " WhichUsers " , " ALL " ) ,
2005-03-14 20:39:40 -04:00
( " ProductLine " , " Python %s %s " % ( major , minor ) ) ,
2004-08-22 10:34:34 -03:00
] )
db . Commit ( )
2010-03-16 15:49:28 -03:00
return db , msiname
2004-08-22 10:34:34 -03:00
def remove_old_versions ( db ) :
" Fill the upgrade table. "
start = " %s . %s .0 " % ( major , minor )
# This requests that feature selection states of an older
# installation should be forwarded into this one. Upgrading
# requires that both the old and the new installation are
# either both per-machine or per-user.
migrate_features = 1
# See "Upgrade Table". We remove releases with the same major and
# minor version. For an snapshot, we remove all earlier snapshots. For
# a release, we remove all snapshots, and all earlier releases.
if snapshot :
add_data ( db , " Upgrade " ,
2004-08-26 02:23:19 -03:00
[ ( upgrade_code_snapshot , start ,
2004-08-22 10:34:34 -03:00
current_version ,
None , # Ignore language
2004-08-26 02:23:19 -03:00
migrate_features ,
2004-08-22 10:34:34 -03:00
None , # Migrate ALL features
" REMOVEOLDSNAPSHOT " ) ] )
props = " REMOVEOLDSNAPSHOT "
else :
add_data ( db , " Upgrade " ,
[ ( upgrade_code , start , current_version ,
None , migrate_features , None , " REMOVEOLDVERSION " ) ,
( upgrade_code_snapshot , start , " %s . %d .0 " % ( major , int ( minor ) + 1 ) ,
None , migrate_features , None , " REMOVEOLDSNAPSHOT " ) ] )
props = " REMOVEOLDSNAPSHOT;REMOVEOLDVERSION "
2008-09-08 10:50:10 -03:00
props + = " ;TARGETDIR;DLLDIR "
2004-08-22 10:34:34 -03:00
# Installer collects the product codes of the earlier releases in
# these properties. In order to allow modification of the properties,
# they must be declared as secure. See "SecureCustomProperties Property"
add_data ( db , " Property " , [ ( " SecureCustomProperties " , props ) ] )
class PyDialog ( Dialog ) :
""" Dialog class with a fixed layout: controls at the top, then a ruler,
then a list of buttons : back , next , cancel . Optionally a bitmap at the
left . """
def __init__ ( self , * args , * * kw ) :
""" Dialog(database, name, x, y, w, h, attributes, title, first,
default , cancel , bitmap = true ) """
Dialog . __init__ ( self , * args )
ruler = self . h - 36
bmwidth = 152 * ruler / 328
if kw . get ( " bitmap " , True ) :
self . bitmap ( " Bitmap " , 0 , 0 , bmwidth , ruler , " PythonWin " )
self . line ( " BottomLine " , 0 , ruler , self . w , 0 )
def title ( self , title ) :
" Set the title text of the dialog at the top. "
# name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix,
# text, in VerdanaBold10
self . text ( " Title " , 135 , 10 , 220 , 60 , 0x30003 ,
r " { \ VerdanaBold10} %s " % title )
def back ( self , title , next , name = " Back " , active = 1 ) :
""" Add a back button with a given title, the tab-next button,
its name in the Control table , possibly initially disabled .
Return the button , so that events can be associated """
if active :
flags = 3 # Visible|Enabled
else :
flags = 1 # Visible
return self . pushbutton ( name , 180 , self . h - 27 , 56 , 17 , flags , title , next )
def cancel ( self , title , next , name = " Cancel " , active = 1 ) :
""" Add a cancel button with a given title, the tab-next button,
its name in the Control table , possibly initially disabled .
Return the button , so that events can be associated """
if active :
flags = 3 # Visible|Enabled
else :
flags = 1 # Visible
return self . pushbutton ( name , 304 , self . h - 27 , 56 , 17 , flags , title , next )
def next ( self , title , next , name = " Next " , active = 1 ) :
""" Add a Next button with a given title, the tab-next button,
its name in the Control table , possibly initially disabled .
Return the button , so that events can be associated """
if active :
flags = 3 # Visible|Enabled
else :
flags = 1 # Visible
return self . pushbutton ( name , 236 , self . h - 27 , 56 , 17 , flags , title , next )
def xbutton ( self , name , title , next , xpos ) :
""" Add a button with a given title, the tab-next button,
its name in the Control table , giving its x position ; the
y - position is aligned with the other buttons .
Return the button , so that events can be associated """
return self . pushbutton ( name , int ( self . w * xpos - 28 ) , self . h - 27 , 56 , 17 , 3 , title , next )
def add_ui ( db ) :
x = y = 50
w = 370
h = 300
title = " [ProductName] Setup "
# see "Dialog Style Bits"
modal = 3 # visible | modal
modeless = 1 # visible
track_disk_space = 32
add_data ( db , ' ActionText ' , uisample . ActionText )
add_data ( db , ' UIText ' , uisample . UIText )
# Bitmaps
if not os . path . exists ( srcdir + r " \ PC \ python_icon.exe " ) :
raise " Run icons.mak in PC directory "
add_data ( db , " Binary " ,
2008-01-01 09:52:57 -04:00
[ ( " PythonWin " , msilib . Binary ( r " %s \ PCbuild \ installer.bmp " % srcdir ) ) , # 152x328 pixels
2004-08-22 10:34:34 -03:00
( " py.ico " , msilib . Binary ( srcdir + r " \ PC \ py.ico " ) ) ,
] )
add_data ( db , " Icon " ,
[ ( " python_icon.exe " , msilib . Binary ( srcdir + r " \ PC \ python_icon.exe " ) ) ] )
# Scripts
2004-09-10 06:20:10 -03:00
# CheckDir sets TargetExists if TARGETDIR exists.
# UpdateEditIDLE sets the REGISTRY.tcl component into
# the installed/uninstalled state according to both the
# Extensions and TclTk features.
2004-12-12 11:29:21 -04:00
add_data ( db , " Binary " , [ ( " Script " , msilib . Binary ( " msisupport.dll " ) ) ] )
# See "Custom Action Type 1"
2005-03-14 13:20:13 -04:00
if msilib . Win64 :
CheckDir = " CheckDir "
2006-02-16 10:38:30 -04:00
UpdateEditIDLE = " UpdateEditIDLE "
2005-03-14 13:20:13 -04:00
else :
CheckDir = " _CheckDir@4 "
UpdateEditIDLE = " _UpdateEditIDLE@4 "
2004-09-12 00:49:31 -03:00
add_data ( db , " CustomAction " ,
2005-03-14 13:20:13 -04:00
[ ( " CheckDir " , 1 , " Script " , CheckDir ) ] )
2004-11-18 04:00:33 -04:00
if have_tcl :
add_data ( db , " CustomAction " ,
2005-03-14 13:20:13 -04:00
[ ( " UpdateEditIDLE " , 1 , " Script " , UpdateEditIDLE ) ] )
2004-08-22 10:34:34 -03:00
# UI customization properties
add_data ( db , " Property " ,
# See "DefaultUIFont Property"
[ ( " DefaultUIFont " , " DlgFont8 " ) ,
# See "ErrorDialog Style Bit"
( " ErrorDialog " , " ErrorDlg " ) ,
( " Progress1 " , " Install " ) , # modified in maintenance type dlg
( " Progress2 " , " installs " ) ,
( " MaintenanceForm_Action " , " Repair " ) ] )
# Fonts, see "TextStyle Table"
add_data ( db , " TextStyle " ,
[ ( " DlgFont8 " , " Tahoma " , 9 , None , 0 ) ,
( " DlgFontBold8 " , " Tahoma " , 8 , None , 1 ) , #bold
( " VerdanaBold10 " , " Verdana " , 10 , None , 1 ) ,
2005-03-14 20:39:40 -04:00
( " VerdanaRed9 " , " Verdana " , 9 , 255 , 0 ) ,
2004-08-22 10:34:34 -03:00
] )
2010-03-15 10:00:17 -03:00
compileargs = r ' -Wi " [TARGETDIR]Lib \ compileall.py " -f -x " bad_coding|badsyntax|site-packages|py3_ " " [TARGETDIR]Lib " '
2008-05-25 13:37:34 -03:00
lib2to3args = r ' -c " import lib2to3.pygram, lib2to3.patcomp;lib2to3.patcomp.PatternCompiler() " '
2004-08-22 10:34:34 -03:00
# See "CustomAction Table"
add_data ( db , " CustomAction " , [
# msidbCustomActionTypeFirstSequence + msidbCustomActionTypeTextData + msidbCustomActionTypeProperty
# See "Custom Action Type 51",
# "Custom Action Execution Scheduling Options"
( " InitialTargetDir " , 307 , " TARGETDIR " ,
" [WindowsVolume]Python %s %s " % ( major , minor ) ) ,
( " SetDLLDirToTarget " , 307 , " DLLDIR " , " [TARGETDIR] " ) ,
( " SetDLLDirToSystem32 " , 307 , " DLLDIR " , SystemFolderName ) ,
# msidbCustomActionTypeExe + msidbCustomActionTypeSourceFile
# See "Custom Action Type 18"
2004-11-02 18:59:56 -04:00
( " CompilePyc " , 18 , " python.exe " , compileargs ) ,
( " CompilePyo " , 18 , " python.exe " , " -O " + compileargs ) ,
2008-05-25 13:37:34 -03:00
( " CompileGrammar " , 18 , " python.exe " , lib2to3args ) ,
2004-08-22 10:34:34 -03:00
] )
# UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"
# Numbers indicate sequence; see sequence.py for how these action integrate
add_data ( db , " InstallUISequence " ,
[ ( " PrepareDlg " , " Not Privileged or Windows9x or Installed " , 140 ) ,
( " WhichUsersDlg " , " Privileged and not Windows9x and not Installed " , 141 ) ,
( " InitialTargetDir " , ' TARGETDIR= " " ' , 750 ) ,
# In the user interface, assume all-users installation if privileged.
( " SetDLLDirToSystem32 " , ' DLLDIR= " " and ' + sys32cond , 751 ) ,
( " SetDLLDirToTarget " , ' DLLDIR= " " and not ' + sys32cond , 752 ) ,
( " SelectDirectoryDlg " , " Not Installed " , 1230 ) ,
# XXX no support for resume installations yet
#("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),
( " MaintenanceTypeDlg " , " Installed AND NOT RESUME AND NOT Preselected " , 1250 ) ,
( " ProgressDlg " , None , 1280 ) ] )
add_data ( db , " AdminUISequence " ,
[ ( " InitialTargetDir " , ' TARGETDIR= " " ' , 750 ) ,
( " SetDLLDirToTarget " , ' DLLDIR= " " ' , 751 ) ,
] )
# Execute Sequences
add_data ( db , " InstallExecuteSequence " ,
[ ( " InitialTargetDir " , ' TARGETDIR= " " ' , 750 ) ,
( " SetDLLDirToSystem32 " , ' DLLDIR= " " and ' + sys32cond , 751 ) ,
( " SetDLLDirToTarget " , ' DLLDIR= " " and not ' + sys32cond , 752 ) ,
2004-09-10 06:20:10 -03:00
( " UpdateEditIDLE " , None , 1050 ) ,
2004-08-22 10:34:34 -03:00
( " CompilePyc " , " COMPILEALL " , 6800 ) ,
( " CompilePyo " , " COMPILEALL " , 6801 ) ,
2008-05-25 13:37:34 -03:00
( " CompileGrammar " , " COMPILEALL " , 6802 ) ,
2004-08-22 10:34:34 -03:00
] )
add_data ( db , " AdminExecuteSequence " ,
[ ( " InitialTargetDir " , ' TARGETDIR= " " ' , 750 ) ,
( " SetDLLDirToTarget " , ' DLLDIR= " " ' , 751 ) ,
( " CompilePyc " , " COMPILEALL " , 6800 ) ,
( " CompilePyo " , " COMPILEALL " , 6801 ) ,
2008-05-25 13:37:34 -03:00
( " CompileGrammar " , " COMPILEALL " , 6802 ) ,
2004-08-22 10:34:34 -03:00
] )
#####################################################################
# Standard dialogs: FatalError, UserExit, ExitDialog
fatal = PyDialog ( db , " FatalError " , x , y , w , h , modal , title ,
" Finish " , " Finish " , " Finish " )
fatal . title ( " [ProductName] Installer ended prematurely " )
fatal . back ( " < Back " , " Finish " , active = 0 )
fatal . cancel ( " Cancel " , " Back " , active = 0 )
fatal . text ( " Description1 " , 135 , 70 , 220 , 80 , 0x30003 ,
" [ProductName] setup ended prematurely because of an error. Your system has not been modified. To install this program at a later time, please run the installation again. " )
fatal . text ( " Description2 " , 135 , 155 , 220 , 20 , 0x30003 ,
" Click the Finish button to exit the Installer. " )
c = fatal . next ( " Finish " , " Cancel " , name = " Finish " )
# See "ControlEvent Table". Parameters are the event, the parameter
# to the action, and optionally the condition for the event, and the order
# of events.
c . event ( " EndDialog " , " Exit " )
2004-08-26 02:23:19 -03:00
2004-08-22 10:34:34 -03:00
user_exit = PyDialog ( db , " UserExit " , x , y , w , h , modal , title ,
" Finish " , " Finish " , " Finish " )
user_exit . title ( " [ProductName] Installer was interrupted " )
user_exit . back ( " < Back " , " Finish " , active = 0 )
user_exit . cancel ( " Cancel " , " Back " , active = 0 )
user_exit . text ( " Description1 " , 135 , 70 , 220 , 80 , 0x30003 ,
" [ProductName] setup was interrupted. Your system has not been modified. "
" To install this program at a later time, please run the installation again. " )
user_exit . text ( " Description2 " , 135 , 155 , 220 , 20 , 0x30003 ,
" Click the Finish button to exit the Installer. " )
c = user_exit . next ( " Finish " , " Cancel " , name = " Finish " )
c . event ( " EndDialog " , " Exit " )
2004-08-26 02:23:19 -03:00
2004-08-22 10:34:34 -03:00
exit_dialog = PyDialog ( db , " ExitDialog " , x , y , w , h , modal , title ,
" Finish " , " Finish " , " Finish " )
2012-02-21 12:07:32 -04:00
exit_dialog . title ( " Complete the [ProductName] Installer " )
2004-08-22 10:34:34 -03:00
exit_dialog . back ( " < Back " , " Finish " , active = 0 )
exit_dialog . cancel ( " Cancel " , " Back " , active = 0 )
2004-08-22 14:10:12 -03:00
exit_dialog . text ( " Acknowledgements " , 135 , 95 , 220 , 120 , 0x30003 ,
" Special Windows thanks to: \n "
2004-08-30 06:22:30 -03:00
" Mark Hammond, without whose years of freely \n "
" shared Windows expertise, Python for Windows \n "
" would still be Python for DOS. " )
2004-08-26 02:23:19 -03:00
2006-03-05 10:04:26 -04:00
c = exit_dialog . text ( " warning " , 135 , 200 , 220 , 40 , 0x30003 ,
2011-05-03 15:36:34 -03:00
" { \\ VerdanaRed9}Warning: Python 3.3.0 is the last "
2010-06-04 16:15:32 -03:00
" Python release for Windows 2000. " )
c . condition ( " Hide " , " VersionNT > 500 " )
2006-03-05 10:04:26 -04:00
2004-08-22 14:10:12 -03:00
exit_dialog . text ( " Description " , 135 , 235 , 220 , 20 , 0x30003 ,
2004-08-22 10:34:34 -03:00
" Click the Finish button to exit the Installer. " )
c = exit_dialog . next ( " Finish " , " Cancel " , name = " Finish " )
c . event ( " EndDialog " , " Return " )
#####################################################################
# Required dialog: FilesInUse, ErrorDlg
inuse = PyDialog ( db , " FilesInUse " ,
x , y , w , h ,
19 , # KeepModeless|Modal|Visible
title ,
" Retry " , " Retry " , " Retry " , bitmap = False )
inuse . text ( " Title " , 15 , 6 , 200 , 15 , 0x30003 ,
r " { \ DlgFontBold8}Files in Use " )
inuse . text ( " Description " , 20 , 23 , 280 , 20 , 0x30003 ,
" Some files that need to be updated are currently in use. " )
inuse . text ( " Text " , 20 , 55 , 330 , 50 , 3 ,
" The following applications are using files that need to be updated by this setup. Close these applications and then click Retry to continue the installation or Cancel to exit it. " )
inuse . control ( " List " , " ListBox " , 20 , 107 , 330 , 130 , 7 , " FileInUseProcess " ,
None , None , None )
c = inuse . back ( " Exit " , " Ignore " , name = " Exit " )
c . event ( " EndDialog " , " Exit " )
c = inuse . next ( " Ignore " , " Retry " , name = " Ignore " )
c . event ( " EndDialog " , " Ignore " )
c = inuse . cancel ( " Retry " , " Exit " , name = " Retry " )
c . event ( " EndDialog " , " Retry " )
# See "Error Dialog". See "ICE20" for the required names of the controls.
error = Dialog ( db , " ErrorDlg " ,
50 , 10 , 330 , 101 ,
65543 , # Error|Minimize|Modal|Visible
title ,
" ErrorText " , None , None )
error . text ( " ErrorText " , 50 , 9 , 280 , 48 , 3 , " " )
error . control ( " ErrorIcon " , " Icon " , 15 , 9 , 24 , 24 , 5242881 , None , " py.ico " , None , None )
error . pushbutton ( " N " , 120 , 72 , 81 , 21 , 3 , " No " , None ) . event ( " EndDialog " , " ErrorNo " )
error . pushbutton ( " Y " , 240 , 72 , 81 , 21 , 3 , " Yes " , None ) . event ( " EndDialog " , " ErrorYes " )
error . pushbutton ( " A " , 0 , 72 , 81 , 21 , 3 , " Abort " , None ) . event ( " EndDialog " , " ErrorAbort " )
error . pushbutton ( " C " , 42 , 72 , 81 , 21 , 3 , " Cancel " , None ) . event ( " EndDialog " , " ErrorCancel " )
error . pushbutton ( " I " , 81 , 72 , 81 , 21 , 3 , " Ignore " , None ) . event ( " EndDialog " , " ErrorIgnore " )
error . pushbutton ( " O " , 159 , 72 , 81 , 21 , 3 , " Ok " , None ) . event ( " EndDialog " , " ErrorOk " )
error . pushbutton ( " R " , 198 , 72 , 81 , 21 , 3 , " Retry " , None ) . event ( " EndDialog " , " ErrorRetry " )
#####################################################################
# Global "Query Cancel" dialog
cancel = Dialog ( db , " CancelDlg " , 50 , 10 , 260 , 85 , 3 , title ,
" No " , " No " , " No " )
2004-08-26 02:23:19 -03:00
cancel . text ( " Text " , 48 , 15 , 194 , 30 , 3 ,
2004-08-22 10:34:34 -03:00
" Are you sure you want to cancel [ProductName] installation? " )
cancel . control ( " Icon " , " Icon " , 15 , 15 , 24 , 24 , 5242881 , None ,
" py.ico " , None , None )
c = cancel . pushbutton ( " Yes " , 72 , 57 , 56 , 17 , 3 , " Yes " , " No " )
c . event ( " EndDialog " , " Exit " )
2004-08-26 02:23:19 -03:00
2004-08-22 10:34:34 -03:00
c = cancel . pushbutton ( " No " , 132 , 57 , 56 , 17 , 3 , " No " , " Yes " )
c . event ( " EndDialog " , " Return " )
#####################################################################
# Global "Wait for costing" dialog
costing = Dialog ( db , " WaitForCostingDlg " , 50 , 10 , 260 , 85 , modal , title ,
" Return " , " Return " , " Return " )
costing . text ( " Text " , 48 , 15 , 194 , 30 , 3 ,
" Please wait while the installer finishes determining your disk space requirements. " )
costing . control ( " Icon " , " Icon " , 15 , 15 , 24 , 24 , 5242881 , None ,
" py.ico " , None , None )
c = costing . pushbutton ( " Return " , 102 , 57 , 56 , 17 , 3 , " Return " , None )
c . event ( " EndDialog " , " Exit " )
#####################################################################
# Preparation dialog: no user input except cancellation
prep = PyDialog ( db , " PrepareDlg " , x , y , w , h , modeless , title ,
" Cancel " , " Cancel " , " Cancel " )
prep . text ( " Description " , 135 , 70 , 220 , 40 , 0x30003 ,
" Please wait while the Installer prepares to guide you through the installation. " )
prep . title ( " Welcome to the [ProductName] Installer " )
c = prep . text ( " ActionText " , 135 , 110 , 220 , 20 , 0x30003 , " Pondering... " )
c . mapping ( " ActionText " , " Text " )
c = prep . text ( " ActionData " , 135 , 135 , 220 , 30 , 0x30003 , None )
c . mapping ( " ActionData " , " Text " )
prep . back ( " Back " , None , active = 0 )
prep . next ( " Next " , None , active = 0 )
c = prep . cancel ( " Cancel " , None )
c . event ( " SpawnDialog " , " CancelDlg " )
#####################################################################
# Target directory selection
seldlg = PyDialog ( db , " SelectDirectoryDlg " , x , y , w , h , modal , title ,
" Next " , " Next " , " Cancel " )
seldlg . title ( " Select Destination Directory " )
2005-03-14 20:39:40 -04:00
c = seldlg . text ( " Existing " , 135 , 25 , 235 , 30 , 0x30003 ,
" { \ VerdanaRed9}This update will replace your existing [ProductLine] installation. " )
c . condition ( " Hide " , ' REMOVEOLDVERSION= " " and REMOVEOLDSNAPSHOT= " " ' )
2004-08-22 10:34:34 -03:00
seldlg . text ( " Description " , 135 , 50 , 220 , 40 , 0x30003 ,
" Please select a directory for the [ProductName] files. " )
seldlg . back ( " < Back " , None , active = 0 )
c = seldlg . next ( " Next > " , " Cancel " )
c . event ( " DoAction " , " CheckDir " , " TargetExistsOk<>1 " , order = 1 )
# If the target exists, but we found that we are going to remove old versions, don't bother
# confirming that the target directory exists. Strictly speaking, we should determine that
# the target directory is indeed the target of the product that we are going to remove, but
# I don't know how to do that.
c . event ( " SpawnDialog " , " ExistingDirectoryDlg " , ' TargetExists=1 and REMOVEOLDVERSION= " " and REMOVEOLDSNAPSHOT= " " ' , 2 )
c . event ( " SetTargetPath " , " TARGETDIR " , ' TargetExists=0 or REMOVEOLDVERSION<> " " or REMOVEOLDSNAPSHOT<> " " ' , 3 )
c . event ( " SpawnWaitDialog " , " WaitForCostingDlg " , " CostingComplete=1 " , 4 )
c . event ( " NewDialog " , " SelectFeaturesDlg " , ' TargetExists=0 or REMOVEOLDVERSION<> " " or REMOVEOLDSNAPSHOT<> " " ' , 5 )
c = seldlg . cancel ( " Cancel " , " DirectoryCombo " )
c . event ( " SpawnDialog " , " CancelDlg " )
seldlg . control ( " DirectoryCombo " , " DirectoryCombo " , 135 , 70 , 172 , 80 , 393219 ,
" TARGETDIR " , None , " DirectoryList " , None )
seldlg . control ( " DirectoryList " , " DirectoryList " , 135 , 90 , 208 , 136 , 3 , " TARGETDIR " ,
None , " PathEdit " , None )
seldlg . control ( " PathEdit " , " PathEdit " , 135 , 230 , 206 , 16 , 3 , " TARGETDIR " , None , " Next " , None )
c = seldlg . pushbutton ( " Up " , 306 , 70 , 18 , 18 , 3 , " Up " , None )
c . event ( " DirectoryListUp " , " 0 " )
c = seldlg . pushbutton ( " NewDir " , 324 , 70 , 30 , 18 , 3 , " New " , None )
c . event ( " DirectoryListNew " , " 0 " )
#####################################################################
# SelectFeaturesDlg
features = PyDialog ( db , " SelectFeaturesDlg " , x , y , w , h , modal | track_disk_space ,
title , " Tree " , " Next " , " Cancel " )
features . title ( " Customize [ProductName] " )
features . text ( " Description " , 135 , 35 , 220 , 15 , 0x30003 ,
" Select the way you want features to be installed. " )
features . text ( " Text " , 135 , 45 , 220 , 30 , 3 ,
" Click on the icons in the tree below to change the way features will be installed. " )
c = features . back ( " < Back " , " Next " )
c . event ( " NewDialog " , " SelectDirectoryDlg " )
c = features . next ( " Next > " , " Cancel " )
c . mapping ( " SelectionNoItems " , " Enabled " )
c . event ( " SpawnDialog " , " DiskCostDlg " , " OutOfDiskSpace=1 " , order = 1 )
c . event ( " EndDialog " , " Return " , " OutOfDiskSpace<>1 " , order = 2 )
c = features . cancel ( " Cancel " , " Tree " )
c . event ( " SpawnDialog " , " CancelDlg " )
2004-08-26 02:23:19 -03:00
# The browse property is not used, since we have only a single target path (selected already)
2004-08-22 10:34:34 -03:00
features . control ( " Tree " , " SelectionTree " , 135 , 75 , 220 , 95 , 7 , " _BrowseProperty " ,
" Tree of selections " , " Back " , None )
#c=features.pushbutton("Reset", 42, 243, 56, 17, 3, "Reset", "DiskCost")
#c.mapping("SelectionNoItems", "Enabled")
#c.event("Reset", "0")
2004-08-26 02:23:19 -03:00
2004-08-22 10:34:34 -03:00
features . control ( " Box " , " GroupBox " , 135 , 170 , 225 , 90 , 1 , None , None , None , None )
c = features . xbutton ( " DiskCost " , " Disk &Usage " , None , 0.10 )
c . mapping ( " SelectionNoItems " , " Enabled " )
c . event ( " SpawnDialog " , " DiskCostDlg " )
c = features . xbutton ( " Advanced " , " Advanced " , None , 0.30 )
c . event ( " SpawnDialog " , " AdvancedDlg " )
c = features . text ( " ItemDescription " , 140 , 180 , 210 , 30 , 3 ,
" Multiline description of the currently selected item. " )
c . mapping ( " SelectionDescription " , " Text " )
2004-08-26 02:23:19 -03:00
2004-08-22 10:34:34 -03:00
c = features . text ( " ItemSize " , 140 , 210 , 210 , 45 , 3 ,
" The size of the currently selected item. " )
c . mapping ( " SelectionSize " , " Text " )
#####################################################################
# Disk cost
cost = PyDialog ( db , " DiskCostDlg " , x , y , w , h , modal , title ,
" OK " , " OK " , " OK " , bitmap = False )
cost . text ( " Title " , 15 , 6 , 200 , 15 , 0x30003 ,
" { \ DlgFontBold8}Disk Space Requirements " )
cost . text ( " Description " , 20 , 20 , 280 , 20 , 0x30003 ,
" The disk space required for the installation of the selected features. " )
cost . text ( " Text " , 20 , 53 , 330 , 60 , 3 ,
" The highlighted volumes (if any) do not have enough disk space "
" available for the currently selected features. You can either "
" remove some files from the highlighted volumes, or choose to "
" install less features onto local drive(s), or select different "
" destination drive(s). " )
cost . control ( " VolumeList " , " VolumeCostList " , 20 , 100 , 330 , 150 , 393223 ,
None , " {120} {70} {70} {70} {70} " , None , None )
cost . xbutton ( " OK " , " Ok " , None , 0.5 ) . event ( " EndDialog " , " Return " )
#####################################################################
# WhichUsers Dialog. Only available on NT, and for privileged users.
# This must be run before FindRelatedProducts, because that will
# take into account whether the previous installation was per-user
# or per-machine. We currently don't support going back to this
# dialog after "Next" was selected; to support this, we would need to
# find how to reset the ALLUSERS property, and how to re-run
# FindRelatedProducts.
# On Windows9x, the ALLUSERS property is ignored on the command line
# and in the Property table, but installer fails according to the documentation
# if a dialog attempts to set ALLUSERS.
whichusers = PyDialog ( db , " WhichUsersDlg " , x , y , w , h , modal , title ,
" AdminInstall " , " Next " , " Cancel " )
whichusers . title ( " Select whether to install [ProductName] for all users of this computer. " )
# A radio group with two options: allusers, justme
2008-10-13 08:23:35 -03:00
g = whichusers . radiogroup ( " AdminInstall " , 135 , 60 , 235 , 80 , 3 ,
2004-08-22 10:34:34 -03:00
" WhichUsers " , " " , " Next " )
2008-10-13 08:23:35 -03:00
g . condition ( " Disable " , " VersionNT=600 " ) # Not available on Vista and Windows 2008
2004-08-22 10:34:34 -03:00
g . add ( " ALL " , 0 , 5 , 150 , 20 , " Install for all users " )
2008-10-13 08:23:35 -03:00
g . add ( " JUSTME " , 0 , 25 , 235 , 20 , " Install just for me (not available on Windows Vista) " )
2004-08-22 10:34:34 -03:00
2004-08-26 02:23:19 -03:00
whichusers . back ( " Back " , None , active = 0 )
2004-08-22 10:34:34 -03:00
c = whichusers . next ( " Next > " , " Cancel " )
c . event ( " [ALLUSERS] " , " 1 " , ' WhichUsers= " ALL " ' , 1 )
c . event ( " EndDialog " , " Return " , order = 2 )
c = whichusers . cancel ( " Cancel " , " AdminInstall " )
c . event ( " SpawnDialog " , " CancelDlg " )
2004-08-26 02:23:19 -03:00
2004-08-22 10:34:34 -03:00
#####################################################################
# Advanced Dialog.
advanced = PyDialog ( db , " AdvancedDlg " , x , y , w , h , modal , title ,
2008-11-19 09:51:44 -04:00
" CompilePyc " , " Ok " , " Ok " )
2004-08-22 10:34:34 -03:00
advanced . title ( " Advanced Options for [ProductName] " )
# A radio group with two options: allusers, justme
advanced . checkbox ( " CompilePyc " , 135 , 60 , 230 , 50 , 3 ,
2008-11-19 09:51:44 -04:00
" COMPILEALL " , " Compile .py files to byte code after installation " , " Ok " )
2004-08-22 10:34:34 -03:00
2008-11-19 09:51:44 -04:00
c = advanced . cancel ( " Ok " , " CompilePyc " , name = " Ok " ) # Button just has location of cancel button.
2004-08-22 10:34:34 -03:00
c . event ( " EndDialog " , " Return " )
#####################################################################
2004-08-26 02:23:19 -03:00
# Existing Directory dialog
2004-08-22 10:34:34 -03:00
dlg = Dialog ( db , " ExistingDirectoryDlg " , 50 , 30 , 200 , 80 , modal , title ,
" No " , " No " , " No " )
dlg . text ( " Title " , 10 , 20 , 180 , 40 , 3 ,
" [TARGETDIR] exists. Are you sure you want to overwrite existing files? " )
c = dlg . pushbutton ( " Yes " , 30 , 60 , 55 , 17 , 3 , " Yes " , " No " )
c . event ( " [TargetExists] " , " 0 " , order = 1 )
c . event ( " [TargetExistsOk] " , " 1 " , order = 2 )
c . event ( " EndDialog " , " Return " , order = 3 )
c = dlg . pushbutton ( " No " , 115 , 60 , 55 , 17 , 3 , " No " , " Yes " )
c . event ( " EndDialog " , " Return " )
#####################################################################
# Installation Progress dialog (modeless)
progress = PyDialog ( db , " ProgressDlg " , x , y , w , h , modeless , title ,
" Cancel " , " Cancel " , " Cancel " , bitmap = False )
progress . text ( " Title " , 20 , 15 , 200 , 15 , 0x30003 ,
" { \ DlgFontBold8}[Progress1] [ProductName] " )
progress . text ( " Text " , 35 , 65 , 300 , 30 , 3 ,
" Please wait while the Installer [Progress2] [ProductName]. "
" This may take several minutes. " )
progress . text ( " StatusLabel " , 35 , 100 , 35 , 20 , 3 , " Status: " )
c = progress . text ( " ActionText " , 70 , 100 , w - 70 , 20 , 3 , " Pondering... " )
c . mapping ( " ActionText " , " Text " )
#c=progress.text("ActionData", 35, 140, 300, 20, 3, None)
#c.mapping("ActionData", "Text")
c = progress . control ( " ProgressBar " , " ProgressBar " , 35 , 120 , 300 , 10 , 65537 ,
None , " Progress done " , None , None )
c . mapping ( " SetProgress " , " Progress " )
progress . back ( " < Back " , " Next " , active = False )
progress . next ( " Next > " , " Cancel " , active = False )
progress . cancel ( " Cancel " , " Back " ) . event ( " SpawnDialog " , " CancelDlg " )
# Maintenance type: repair/uninstall
maint = PyDialog ( db , " MaintenanceTypeDlg " , x , y , w , h , modal , title ,
" Next " , " Next " , " Cancel " )
maint . title ( " Welcome to the [ProductName] Setup Wizard " )
maint . text ( " BodyText " , 135 , 63 , 230 , 42 , 3 ,
" Select whether you want to repair or remove [ProductName]. " )
g = maint . radiogroup ( " RepairRadioGroup " , 135 , 108 , 230 , 60 , 3 ,
" MaintenanceForm_Action " , " " , " Next " )
g . add ( " Change " , 0 , 0 , 200 , 17 , " &Change [ProductName] " )
g . add ( " Repair " , 0 , 18 , 200 , 17 , " &Repair [ProductName] " )
g . add ( " Remove " , 0 , 36 , 200 , 17 , " Re&move [ProductName] " )
2004-08-26 02:23:19 -03:00
2004-08-22 10:34:34 -03:00
maint . back ( " < Back " , None , active = False )
c = maint . next ( " Finish " , " Cancel " )
# Change installation: Change progress dialog to "Change", then ask
# for feature selection
c . event ( " [Progress1] " , " Change " , ' MaintenanceForm_Action= " Change " ' , 1 )
c . event ( " [Progress2] " , " changes " , ' MaintenanceForm_Action= " Change " ' , 2 )
# Reinstall: Change progress dialog to "Repair", then invoke reinstall
# Also set list of reinstalled features to "ALL"
c . event ( " [REINSTALL] " , " ALL " , ' MaintenanceForm_Action= " Repair " ' , 5 )
c . event ( " [Progress1] " , " Repairing " , ' MaintenanceForm_Action= " Repair " ' , 6 )
2004-11-07 03:08:25 -04:00
c . event ( " [Progress2] " , " repairs " , ' MaintenanceForm_Action= " Repair " ' , 7 )
2004-08-22 10:34:34 -03:00
c . event ( " Reinstall " , " ALL " , ' MaintenanceForm_Action= " Repair " ' , 8 )
# Uninstall: Change progress to "Remove", then invoke uninstall
# Also set list of removed features to "ALL"
c . event ( " [REMOVE] " , " ALL " , ' MaintenanceForm_Action= " Remove " ' , 11 )
c . event ( " [Progress1] " , " Removing " , ' MaintenanceForm_Action= " Remove " ' , 12 )
c . event ( " [Progress2] " , " removes " , ' MaintenanceForm_Action= " Remove " ' , 13 )
c . event ( " Remove " , " ALL " , ' MaintenanceForm_Action= " Remove " ' , 14 )
2004-08-26 02:23:19 -03:00
# Close dialog when maintenance action scheduled
2004-08-22 10:34:34 -03:00
c . event ( " EndDialog " , " Return " , ' MaintenanceForm_Action<> " Change " ' , 20 )
c . event ( " NewDialog " , " SelectFeaturesDlg " , ' MaintenanceForm_Action= " Change " ' , 21 )
2004-08-26 02:23:19 -03:00
2004-08-22 10:34:34 -03:00
maint . cancel ( " Cancel " , " RepairRadioGroup " ) . event ( " SpawnDialog " , " CancelDlg " )
2004-08-26 02:23:19 -03:00
2004-08-22 10:34:34 -03:00
# See "Feature Table". The feature level is 1 for all features,
# and the feature attributes are 0 for the DefaultFeature, and
# FollowParent for all other features. The numbers are the Display
# column.
def add_features ( db ) :
# feature attributes:
# msidbFeatureAttributesFollowParent == 2
# msidbFeatureAttributesDisallowAdvertise == 8
# Features that need to be installed with together with the main feature
# (i.e. additional Python libraries) need to follow the parent feature.
# Features that have no advertisement trigger (e.g. the test suite)
# must not support advertisement
2008-04-07 11:54:16 -03:00
global default_feature , tcltk , htmlfiles , tools , testsuite , ext_feature , private_crt
2004-08-22 10:34:34 -03:00
default_feature = Feature ( db , " DefaultFeature " , " Python " ,
" Python Interpreter and Libraries " ,
1 , directory = " TARGETDIR " )
2008-04-05 12:48:36 -03:00
shared_crt = Feature ( db , " SharedCRT " , " MSVCRT " , " C Run-Time (system-wide) " , 0 ,
level = 0 )
private_crt = Feature ( db , " PrivateCRT " , " MSVCRT " , " C Run-Time (private) " , 0 ,
level = 0 )
add_data ( db , " Condition " , [ ( " SharedCRT " , 1 , sys32cond ) ,
( " PrivateCRT " , 1 , " not " + sys32cond ) ] )
2004-09-10 06:20:10 -03:00
# We don't support advertisement of extensions
2004-08-22 10:34:34 -03:00
ext_feature = Feature ( db , " Extensions " , " Register Extensions " ,
" Make this Python installation the default Python installation " , 3 ,
2004-09-10 06:20:10 -03:00
parent = default_feature , attributes = 2 | 8 )
2004-09-01 11:51:06 -03:00
if have_tcl :
tcltk = Feature ( db , " TclTk " , " Tcl/Tk " , " Tkinter, IDLE, pydoc " , 5 ,
2004-08-22 10:34:34 -03:00
parent = default_feature , attributes = 2 )
htmlfiles = Feature ( db , " Documentation " , " Documentation " ,
" Python HTMLHelp File " , 7 , parent = default_feature )
tools = Feature ( db , " Tools " , " Utility Scripts " ,
2004-08-26 02:23:19 -03:00
" Python utility scripts (Tools/ " , 9 ,
2004-08-22 10:34:34 -03:00
parent = default_feature , attributes = 2 )
testsuite = Feature ( db , " Testsuite " , " Test suite " ,
" Python test suite (Lib/test/) " , 11 ,
parent = default_feature , attributes = 2 | 8 )
2004-08-26 02:23:19 -03:00
2007-12-04 10:57:30 -04:00
def extract_msvcr90 ( ) :
2008-02-28 18:20:50 -04:00
# Find the redistributable files
2008-09-19 16:20:03 -03:00
if msilib . Win64 :
arch = " amd64 "
else :
arch = " x86 "
dir = os . path . join ( os . environ [ ' VS90COMNTOOLS ' ] , r " .. \ .. \ VC \ redist \ %s \ Microsoft.VC90.CRT " % arch )
2007-12-04 10:57:30 -04:00
2008-02-28 15:57:34 -04:00
result = [ ]
2007-12-04 10:57:30 -04:00
installer = msilib . MakeInstaller ( )
2008-02-28 15:57:34 -04:00
# omit msvcm90 and msvcp90, as they aren't really needed
files = [ " Microsoft.VC90.CRT.manifest " , " msvcr90.dll " ]
for f in files :
path = os . path . join ( dir , f )
kw = { ' src ' : path }
if f . endswith ( ' .dll ' ) :
kw [ ' version ' ] = installer . FileVersion ( path , 0 )
kw [ ' language ' ] = installer . FileVersion ( path , 1 )
result . append ( ( f , kw ) )
return result
2007-12-04 10:57:30 -04:00
2008-05-25 08:56:23 -03:00
def generate_license ( ) :
import shutil , glob
out = open ( " LICENSE.txt " , " w " )
shutil . copyfileobj ( open ( os . path . join ( srcdir , " LICENSE " ) ) , out )
2008-09-14 17:22:39 -03:00
shutil . copyfileobj ( open ( " crtlicense.txt " ) , out )
2008-06-12 17:07:53 -03:00
for name , pat , file in ( ( " bzip2 " , " bzip2-* " , " LICENSE " ) ,
( " Berkeley DB " , " db-* " , " LICENSE " ) ,
( " openssl " , " openssl-* " , " LICENSE " ) ,
( " Tcl " , " tcl8* " , " license.terms " ) ,
( " Tk " , " tk8* " , " license.terms " ) ,
( " Tix " , " tix-* " , " license.terms " ) ) :
out . write ( " \n This copy of Python includes a copy of %s , which is licensed under the following terms: \n \n " % name )
dirs = glob . glob ( srcdir + " /../ " + pat )
2008-05-25 08:56:23 -03:00
if not dirs :
2008-06-12 17:07:53 -03:00
raise ValueError , " Could not find " + srcdir + " /../ " + pat
2008-05-25 08:56:23 -03:00
if len ( dirs ) > 2 :
2008-06-12 17:07:53 -03:00
raise ValueError , " Multiple copies of " + pat
2008-05-25 08:56:23 -03:00
dir = dirs [ 0 ]
shutil . copyfileobj ( open ( os . path . join ( dir , file ) ) , out )
out . close ( )
2004-08-22 10:34:34 -03:00
class PyDirectory ( Directory ) :
""" By default, all components in the Python installer
can run from source . """
def __init__ ( self , * args , * * kw ) :
if not kw . has_key ( " componentflags " ) :
kw [ ' componentflags ' ] = 2 #msidbComponentAttributesOptional
Directory . __init__ ( self , * args , * * kw )
# See "File Table", "Component Table", "Directory Table",
# "FeatureComponents Table"
def add_files ( db ) :
cab = CAB ( " python " )
tmpfiles = [ ]
# Add all executables, icons, text files into the TARGETDIR component
root = PyDirectory ( db , cab , None , srcdir , " TARGETDIR " , " SourceDir " )
default_feature . set_current ( )
2004-09-01 11:51:06 -03:00
if not msilib . Win64 :
2007-12-04 10:57:30 -04:00
root . add_file ( " %s /w9xpopen.exe " % PCBUILD )
2004-08-22 10:34:34 -03:00
root . add_file ( " README.txt " , src = " README " )
root . add_file ( " NEWS.txt " , src = " Misc/NEWS " )
2008-05-25 08:56:23 -03:00
generate_license ( )
root . add_file ( " LICENSE.txt " , src = os . path . abspath ( " LICENSE.txt " ) )
2004-08-22 10:34:34 -03:00
root . start_component ( " python.exe " , keyfile = " python.exe " )
2007-12-04 10:57:30 -04:00
root . add_file ( " %s /python.exe " % PCBUILD )
2004-08-22 10:34:34 -03:00
root . start_component ( " pythonw.exe " , keyfile = " pythonw.exe " )
2007-12-04 10:57:30 -04:00
root . add_file ( " %s /pythonw.exe " % PCBUILD )
2004-08-22 10:34:34 -03:00
# msidbComponentAttributesSharedDllRefCount = 8, see "Component Table"
2008-10-17 10:43:01 -03:00
dlldir = PyDirectory ( db , cab , root , srcdir , " DLLDIR " , " . " )
2008-02-28 15:57:34 -04:00
2004-08-22 10:34:34 -03:00
pydll = " python %s %s .dll " % ( major , minor )
2007-12-04 10:57:30 -04:00
pydllsrc = os . path . join ( srcdir , PCBUILD , pydll )
2005-03-14 20:39:40 -04:00
dlldir . start_component ( " DLLDIR " , flags = 8 , keyfile = pydll , uuid = pythondll_uuid )
2004-08-22 10:34:34 -03:00
installer = msilib . MakeInstaller ( )
pyversion = installer . FileVersion ( pydllsrc , 0 )
if not snapshot :
# For releases, the Python DLL has the same version as the
# installer package.
assert pyversion . split ( " . " ) [ : 3 ] == current_version . split ( " . " )
2007-12-04 10:57:30 -04:00
dlldir . add_file ( " %s /python %s %s .dll " % ( PCBUILD , major , minor ) ,
2004-08-22 10:34:34 -03:00
version = pyversion ,
language = installer . FileVersion ( pydllsrc , 1 ) )
2008-02-28 15:57:34 -04:00
DLLs = PyDirectory ( db , cab , root , srcdir + " / " + PCBUILD , " DLLs " , " DLLS|DLLs " )
2008-04-07 13:34:04 -03:00
# msvcr90.dll: Need to place the DLL and the manifest into the root directory,
# plus another copy of the manifest in the DLLs directory, with the manifest
# pointing to the root directory
2008-04-07 11:55:53 -03:00
root . start_component ( " msvcr90 " , feature = private_crt )
2008-04-07 13:34:04 -03:00
# Results are ID,keyword pairs
manifest , crtdll = extract_msvcr90 ( )
root . add_file ( manifest [ 0 ] , * * manifest [ 1 ] )
root . add_file ( crtdll [ 0 ] , * * crtdll [ 1 ] )
# Copy the manifest
2008-11-06 15:46:03 -04:00
# Actually, don't do that anymore - no DLL in DLLs should have a manifest
# dependency on msvcr90.dll anymore, so this should not be necessary
#manifest_dlls = manifest[0]+".root"
#open(manifest_dlls, "w").write(open(manifest[1]['src']).read().replace("msvcr","../msvcr"))
#DLLs.start_component("msvcr90_dlls", feature=private_crt)
#DLLs.add_file(manifest[0], src=os.path.abspath(manifest_dlls))
2008-04-07 13:34:04 -03:00
# Now start the main component for the DLLs directory;
# no regular files have been added to the directory yet.
DLLs . start_component ( )
2004-08-26 02:23:19 -03:00
2006-08-24 21:03:34 -03:00
# Check if _ctypes.pyd exists
2007-12-04 10:57:30 -04:00
have_ctypes = os . path . exists ( srcdir + " / %s /_ctypes.pyd " % PCBUILD )
2006-08-24 21:03:34 -03:00
if not have_ctypes :
print " WARNING: _ctypes.pyd not found, ctypes will not be included "
extensions . remove ( " _ctypes.pyd " )
2006-08-25 19:05:39 -03:00
2004-08-22 10:34:34 -03:00
# Add all .py files in Lib, except lib-tk, test
dirs = { }
pydirs = [ ( root , " Lib " ) ]
while pydirs :
2008-06-13 15:58:47 -03:00
# Commit every now and then, or else installer will complain
db . Commit ( )
2004-08-22 10:34:34 -03:00
parent , dir = pydirs . pop ( )
2006-01-03 02:29:53 -04:00
if dir == " .svn " or dir . startswith ( " plat- " ) :
2004-08-22 10:34:34 -03:00
continue
2008-05-20 04:13:37 -03:00
elif dir in [ " lib-tk " , " idlelib " , " Icons " ] :
2004-09-01 11:51:06 -03:00
if not have_tcl :
continue
2004-08-22 10:34:34 -03:00
tcltk . set_current ( )
2004-12-30 10:08:18 -04:00
elif dir in [ ' test ' , ' tests ' , ' data ' , ' output ' ] :
2006-04-03 09:07:46 -03:00
# test: Lib, Lib/email, Lib/bsddb, Lib/ctypes, Lib/sqlite3
2004-12-30 10:08:18 -04:00
# tests: Lib/distutils
# data: Lib/email/test
# output: Lib/test
2004-08-22 10:34:34 -03:00
testsuite . set_current ( )
2006-08-24 21:03:34 -03:00
elif not have_ctypes and dir == " ctypes " :
continue
2004-08-22 10:34:34 -03:00
else :
default_feature . set_current ( )
lib = PyDirectory ( db , cab , parent , dir , dir , " %s | %s " % ( parent . make_short ( dir ) , dir ) )
# Add additional files
dirs [ dir ] = lib
lib . glob ( " *.txt " )
if dir == ' site-packages ' :
2004-11-21 06:16:26 -04:00
lib . add_file ( " README.txt " , src = " README " )
2004-08-22 10:34:34 -03:00
continue
files = lib . glob ( " *.py " )
files + = lib . glob ( " *.pyw " )
if files :
# Add an entry to the RemoveFile table to remove bytecode files.
lib . remove_pyc ( )
2006-04-21 07:00:46 -03:00
if dir . endswith ( ' .egg-info ' ) :
lib . add_file ( ' entry_points.txt ' )
lib . add_file ( ' PKG-INFO ' )
lib . add_file ( ' top_level.txt ' )
lib . add_file ( ' zip-safe ' )
continue
2004-08-22 10:34:34 -03:00
if dir == ' test ' and parent . physical == ' Lib ' :
lib . add_file ( " 185test.db " )
lib . add_file ( " audiotest.au " )
lib . add_file ( " cfgparser.1 " )
2006-09-12 16:49:20 -03:00
lib . add_file ( " sgml_input.html " )
2004-08-22 10:34:34 -03:00
lib . add_file ( " testtar.tar " )
2004-09-06 03:31:12 -03:00
lib . add_file ( " test_difflib_expect.html " )
2006-04-03 09:07:46 -03:00
lib . add_file ( " check_soundcard.vbs " )
2006-04-04 15:41:13 -03:00
lib . add_file ( " empty.vbs " )
2009-06-01 01:10:03 -03:00
lib . add_file ( " Sine-1000Hz-300ms.aif " )
2004-08-22 10:34:34 -03:00
lib . glob ( " *.uue " )
2007-11-19 22:46:02 -04:00
lib . glob ( " *.pem " )
2007-12-03 15:20:02 -04:00
lib . glob ( " *.pck " )
2009-04-04 15:44:44 -03:00
lib . add_file ( " zipdir.zip " )
2010-11-21 22:08:49 -04:00
if dir == ' tests ' and parent . physical == ' distutils ' :
lib . add_file ( " Setup.sample " )
2004-08-22 10:34:34 -03:00
if dir == ' decimaltestdata ' :
lib . glob ( " *.decTest " )
2010-03-13 07:18:49 -04:00
if dir == ' xmltestdata ' :
lib . glob ( " *.xml " )
lib . add_file ( " test.xml.out " )
2004-08-22 10:34:34 -03:00
if dir == ' output ' :
lib . glob ( " test_* " )
if dir == ' idlelib ' :
lib . glob ( " *.def " )
lib . add_file ( " idle.bat " )
if dir == " Icons " :
lib . glob ( " *.gif " )
lib . add_file ( " idle.icns " )
2006-04-21 07:00:46 -03:00
if dir == " command " and parent . physical == " distutils " :
2008-04-09 15:56:20 -03:00
lib . glob ( " wininst*.exe " )
2006-04-21 07:00:46 -03:00
if dir == " setuptools " :
lib . add_file ( " cli.exe " )
lib . add_file ( " gui.exe " )
2008-05-25 13:37:34 -03:00
if dir == " lib2to3 " :
lib . removefile ( " pickle " , " *.pickle " )
2004-08-22 10:34:34 -03:00
if dir == " data " and parent . physical == " test " and parent . basedir . physical == " email " :
2006-01-03 02:29:53 -04:00
# This should contain all non-.svn files listed in subversion
2004-08-22 10:34:34 -03:00
for f in os . listdir ( lib . absolute ) :
2006-01-03 02:29:53 -04:00
if f . endswith ( " .txt " ) or f == " .svn " : continue
2004-08-22 10:34:34 -03:00
if f . endswith ( " .au " ) or f . endswith ( " .gif " ) :
lib . add_file ( f )
else :
print " WARNING: New file %s in email/test/data " % f
for f in os . listdir ( lib . absolute ) :
if os . path . isdir ( os . path . join ( lib . absolute , f ) ) :
pydirs . append ( ( lib , f ) )
# Add DLLs
default_feature . set_current ( )
2008-02-28 15:57:34 -04:00
lib = DLLs
2008-01-01 09:52:57 -04:00
lib . add_file ( " py.ico " , src = srcdir + " /PC/py.ico " )
2008-01-01 09:58:16 -04:00
lib . add_file ( " pyc.ico " , src = srcdir + " /PC/pyc.ico " )
2004-08-22 10:34:34 -03:00
dlls = [ ]
tclfiles = [ ]
for f in extensions :
if f == " _tkinter.pyd " :
continue
2007-12-04 10:57:30 -04:00
if not os . path . exists ( srcdir + " / " + PCBUILD + " / " + f ) :
2004-08-22 10:34:34 -03:00
print " WARNING: Missing extension " , f
continue
dlls . append ( f )
lib . add_file ( f )
2006-07-06 03:55:58 -03:00
# Add sqlite
if msilib . msi_type == " Intel64;1033 " :
sqlite_arch = " /ia64 "
elif msilib . msi_type == " x64;1033 " :
sqlite_arch = " /amd64 "
2008-02-29 16:54:44 -04:00
tclsuffix = " 64 "
2006-07-06 03:55:58 -03:00
else :
sqlite_arch = " "
2008-02-29 16:54:44 -04:00
tclsuffix = " "
2008-06-13 15:58:47 -03:00
lib . add_file ( " sqlite3.dll " )
2004-09-01 11:51:06 -03:00
if have_tcl :
2007-12-04 10:57:30 -04:00
if not os . path . exists ( " %s / %s /_tkinter.pyd " % ( srcdir , PCBUILD ) ) :
2004-09-01 11:51:06 -03:00
print " WARNING: Missing _tkinter.pyd "
else :
lib . start_component ( " TkDLLs " , tcltk )
lib . add_file ( " _tkinter.pyd " )
dlls . append ( " _tkinter.pyd " )
2008-02-29 16:54:44 -04:00
tcldir = os . path . normpath ( srcdir + ( " /../tcltk %s /bin " % tclsuffix ) )
2004-09-01 11:51:06 -03:00
for f in glob . glob1 ( tcldir , " *.dll " ) :
lib . add_file ( f , src = os . path . join ( tcldir , f ) )
2004-08-22 10:34:34 -03:00
# check whether there are any unknown extensions
2007-12-04 10:57:30 -04:00
for f in glob . glob1 ( srcdir + " / " + PCBUILD , " *.pyd " ) :
2004-08-22 10:34:34 -03:00
if f . endswith ( " _d.pyd " ) : continue # debug version
if f in dlls : continue
print " WARNING: Unknown extension " , f
2004-08-26 02:23:19 -03:00
2004-08-22 10:34:34 -03:00
# Add headers
default_feature . set_current ( )
lib = PyDirectory ( db , cab , root , " include " , " include " , " INCLUDE|include " )
lib . glob ( " *.h " )
lib . add_file ( " pyconfig.h " , src = " ../PC/pyconfig.h " )
# Add import libraries
2007-12-04 10:57:30 -04:00
lib = PyDirectory ( db , cab , root , PCBUILD , " libs " , " LIBS|libs " )
2004-08-22 10:34:34 -03:00
for f in dlls :
lib . add_file ( f . replace ( ' pyd ' , ' lib ' ) )
lib . add_file ( ' python %s %s .lib ' % ( major , minor ) )
2004-12-22 09:41:49 -04:00
# Add the mingw-format library
if have_mingw :
2005-01-07 12:01:32 -04:00
lib . add_file ( ' libpython %s %s .a ' % ( major , minor ) )
2004-09-01 11:51:06 -03:00
if have_tcl :
# Add Tcl/Tk
2008-02-29 16:54:44 -04:00
tcldirs = [ ( root , ' ../tcltk %s /lib ' % tclsuffix , ' tcl ' ) ]
2004-09-01 11:51:06 -03:00
tcltk . set_current ( )
while tcldirs :
parent , phys , dir = tcldirs . pop ( )
lib = PyDirectory ( db , cab , parent , phys , dir , " %s | %s " % ( parent . make_short ( dir ) , dir ) )
if not os . path . exists ( lib . absolute ) :
continue
for f in os . listdir ( lib . absolute ) :
if os . path . isdir ( os . path . join ( lib . absolute , f ) ) :
tcldirs . append ( ( lib , f , f ) )
else :
lib . add_file ( f )
2004-08-22 10:34:34 -03:00
# Add tools
tools . set_current ( )
tooldir = PyDirectory ( db , cab , root , " Tools " , " Tools " , " TOOLS|Tools " )
for f in [ ' i18n ' , ' pynche ' , ' Scripts ' , ' versioncheck ' , ' webchecker ' ] :
lib = PyDirectory ( db , cab , tooldir , f , f , " %s | %s " % ( tooldir . make_short ( f ) , f ) )
lib . glob ( " *.py " )
lib . glob ( " *.pyw " , exclude = [ ' pydocgui.pyw ' ] )
lib . remove_pyc ( )
lib . glob ( " *.txt " )
if f == " pynche " :
x = PyDirectory ( db , cab , lib , " X " , " X " , " X|X " )
x . glob ( " *.txt " )
2004-12-01 17:46:35 -04:00
if os . path . exists ( os . path . join ( lib . absolute , " README " ) ) :
2004-08-22 10:34:34 -03:00
lib . add_file ( " README.txt " , src = " README " )
2004-12-01 17:46:35 -04:00
if f == ' Scripts ' :
2008-10-01 08:19:50 -03:00
lib . add_file ( " 2to3.py " , src = " 2to3 " )
2004-09-01 11:51:06 -03:00
if have_tcl :
lib . start_component ( " pydocgui.pyw " , tcltk , keyfile = " pydocgui.pyw " )
lib . add_file ( " pydocgui.pyw " )
2004-08-22 10:34:34 -03:00
# Add documentation
htmlfiles . set_current ( )
lib = PyDirectory ( db , cab , root , " Doc " , " Doc " , " DOC|Doc " )
2008-05-09 15:21:55 -03:00
lib . start_component ( " documentation " , keyfile = docfile )
lib . add_file ( docfile , src = " build/htmlhelp/ " + docfile )
2004-08-22 10:34:34 -03:00
cab . commit ( db )
for f in tmpfiles :
os . unlink ( f )
# See "Registry Table", "Component Table"
def add_registry ( db ) :
# File extensions, associated with the REGISTRY.def component
# IDLE verbs depend on the tcltk feature.
# msidbComponentAttributesRegistryKeyPath = 4
# -1 for Root specifies "dependent on ALLUSERS property"
2004-09-01 11:51:06 -03:00
tcldata = [ ]
if have_tcl :
tcldata = [
2007-08-31 07:01:07 -03:00
( " REGISTRY.tcl " , msilib . gen_uuid ( ) , " TARGETDIR " , registry_component , None ,
2004-09-10 06:20:10 -03:00
" py.IDLE " ) ]
2004-08-22 10:34:34 -03:00
add_data ( db , " Component " ,
# msidbComponentAttributesRegistryKeyPath = 4
2007-08-31 07:01:07 -03:00
[ ( " REGISTRY " , msilib . gen_uuid ( ) , " TARGETDIR " , registry_component , None ,
2004-08-22 10:34:34 -03:00
" InstallPath " ) ,
2007-08-31 07:01:07 -03:00
( " REGISTRY.doc " , msilib . gen_uuid ( ) , " TARGETDIR " , registry_component , None ,
2005-03-14 20:39:40 -04:00
" Documentation " ) ,
2007-08-31 07:01:07 -03:00
( " REGISTRY.def " , msilib . gen_uuid ( ) , " TARGETDIR " , registry_component ,
2004-09-01 11:51:06 -03:00
None , None ) ] + tcldata )
2004-08-22 10:34:34 -03:00
# See "FeatureComponents Table".
# The association between TclTk and pythonw.exe is necessary to make ICE59
# happy, because the installer otherwise believes that the IDLE and PyDoc
# shortcuts might get installed without pythonw.exe being install. This
# is not true, since installing TclTk will install the default feature, which
# will cause pythonw.exe to be installed.
2004-09-10 06:20:10 -03:00
# REGISTRY.tcl is not associated with any feature, as it will be requested
# through a custom action
2004-09-01 11:51:06 -03:00
tcldata = [ ]
if have_tcl :
2004-09-10 06:20:10 -03:00
tcldata = [ ( tcltk . id , " pythonw.exe " ) ]
2004-08-22 10:34:34 -03:00
add_data ( db , " FeatureComponents " ,
[ ( default_feature . id , " REGISTRY " ) ,
2005-03-14 20:39:40 -04:00
( htmlfiles . id , " REGISTRY.doc " ) ,
2004-09-01 11:51:06 -03:00
( ext_feature . id , " REGISTRY.def " ) ] +
tcldata
)
2004-09-10 06:20:10 -03:00
# Extensions are not advertised. For advertised extensions,
# we would need separate binaries that install along with the
# extension.
2004-08-22 10:34:34 -03:00
pat = r " Software \ Classes \ %s Python. %s File \ shell \ %s \ command "
ewi = " Edit with IDLE "
pat2 = r " Software \ Classes \ %s Python. %s File \ DefaultIcon "
pat3 = r " Software \ Classes \ %s Python. %s File "
2008-11-07 14:51:50 -04:00
pat4 = r " Software \ Classes \ %s Python. %s File \ shellex \ DropHandler "
2004-11-18 04:00:33 -04:00
tcl_verbs = [ ]
if have_tcl :
tcl_verbs = [
( " py.IDLE " , - 1 , pat % ( testprefix , " " , ewi ) , " " ,
2009-05-05 13:10:16 -03:00
r ' " [TARGETDIR]pythonw.exe " " [TARGETDIR]Lib \ idlelib \ idle.pyw " -e " % 1 " ' ,
2004-11-18 04:00:33 -04:00
" REGISTRY.tcl " ) ,
( " pyw.IDLE " , - 1 , pat % ( testprefix , " NoCon " , ewi ) , " " ,
2009-05-05 13:10:16 -03:00
r ' " [TARGETDIR]pythonw.exe " " [TARGETDIR]Lib \ idlelib \ idle.pyw " -e " % 1 " ' ,
2004-11-18 04:00:33 -04:00
" REGISTRY.tcl " ) ,
]
2004-09-10 06:20:10 -03:00
add_data ( db , " Registry " ,
[ # Extensions
( " py.ext " , - 1 , r " Software \ Classes \ . " + ext , " " ,
" Python.File " , " REGISTRY.def " ) ,
( " pyw.ext " , - 1 , r " Software \ Classes \ . " + ext + ' w ' , " " ,
" Python.NoConFile " , " REGISTRY.def " ) ,
( " pyc.ext " , - 1 , r " Software \ Classes \ . " + ext + ' c ' , " " ,
" Python.CompiledFile " , " REGISTRY.def " ) ,
( " pyo.ext " , - 1 , r " Software \ Classes \ . " + ext + ' o ' , " " ,
" Python.CompiledFile " , " REGISTRY.def " ) ,
# MIME types
( " py.mime " , - 1 , r " Software \ Classes \ . " + ext , " Content Type " ,
" text/plain " , " REGISTRY.def " ) ,
( " pyw.mime " , - 1 , r " Software \ Classes \ . " + ext + ' w ' , " Content Type " ,
" text/plain " , " REGISTRY.def " ) ,
#Verbs
( " py.open " , - 1 , pat % ( testprefix , " " , " open " ) , " " ,
r ' " [TARGETDIR]python.exe " " % 1 " % * ' , " REGISTRY.def " ) ,
( " pyw.open " , - 1 , pat % ( testprefix , " NoCon " , " open " ) , " " ,
r ' " [TARGETDIR]pythonw.exe " " % 1 " % * ' , " REGISTRY.def " ) ,
( " pyc.open " , - 1 , pat % ( testprefix , " Compiled " , " open " ) , " " ,
r ' " [TARGETDIR]python.exe " " % 1 " % * ' , " REGISTRY.def " ) ,
2004-11-18 04:00:33 -04:00
] + tcl_verbs + [
2004-09-10 06:20:10 -03:00
#Icons
( " py.icon " , - 1 , pat2 % ( testprefix , " " ) , " " ,
2006-05-12 10:57:36 -03:00
r ' [DLLs]py.ico ' , " REGISTRY.def " ) ,
2004-09-10 06:20:10 -03:00
( " pyw.icon " , - 1 , pat2 % ( testprefix , " NoCon " ) , " " ,
2006-05-12 10:57:36 -03:00
r ' [DLLs]py.ico ' , " REGISTRY.def " ) ,
2004-09-10 06:20:10 -03:00
( " pyc.icon " , - 1 , pat2 % ( testprefix , " Compiled " ) , " " ,
2006-05-12 10:57:36 -03:00
r ' [DLLs]pyc.ico ' , " REGISTRY.def " ) ,
2004-09-10 06:20:10 -03:00
# Descriptions
( " py.txt " , - 1 , pat3 % ( testprefix , " " ) , " " ,
" Python File " , " REGISTRY.def " ) ,
( " pyw.txt " , - 1 , pat3 % ( testprefix , " NoCon " ) , " " ,
" Python File (no console) " , " REGISTRY.def " ) ,
( " pyc.txt " , - 1 , pat3 % ( testprefix , " Compiled " ) , " " ,
" Compiled Python File " , " REGISTRY.def " ) ,
2008-11-07 14:51:50 -04:00
# Drop Handler
( " py.drop " , - 1 , pat4 % ( testprefix , " " ) , " " ,
" { 60254CA5-953B-11CF-8C96-00AA00B8708C} " , " REGISTRY.def " ) ,
( " pyw.drop " , - 1 , pat4 % ( testprefix , " NoCon " ) , " " ,
" { 60254CA5-953B-11CF-8C96-00AA00B8708C} " , " REGISTRY.def " ) ,
( " pyc.drop " , - 1 , pat4 % ( testprefix , " Compiled " ) , " " ,
" { 60254CA5-953B-11CF-8C96-00AA00B8708C} " , " REGISTRY.def " ) ,
2004-08-22 10:34:34 -03:00
] )
2004-08-26 02:23:19 -03:00
2004-08-22 10:34:34 -03:00
# Registry keys
prefix = r " Software \ %s Python \ PythonCore \ %s " % ( testprefix , short_version )
add_data ( db , " Registry " ,
[ ( " InstallPath " , - 1 , prefix + r " \ InstallPath " , " " , " [TARGETDIR] " , " REGISTRY " ) ,
( " InstallGroup " , - 1 , prefix + r " \ InstallPath \ InstallGroup " , " " ,
" Python %s " % short_version , " REGISTRY " ) ,
( " PythonPath " , - 1 , prefix + r " \ PythonPath " , " " ,
2004-09-19 15:36:45 -03:00
r " [TARGETDIR]Lib;[TARGETDIR]DLLs;[TARGETDIR]Lib \ lib-tk " , " REGISTRY " ) ,
2004-08-22 10:34:34 -03:00
( " Documentation " , - 1 , prefix + r " \ Help \ Main Python Documentation " , " " ,
2008-05-09 15:21:55 -03:00
" [TARGETDIR]Doc \\ " + docfile , " REGISTRY.doc " ) ,
2004-08-22 10:34:34 -03:00
( " Modules " , - 1 , prefix + r " \ Modules " , " + " , None , " REGISTRY " ) ,
( " AppPaths " , - 1 , r " Software \ Microsoft \ Windows \ CurrentVersion \ App Paths \ Python.exe " ,
2008-11-30 07:08:26 -04:00
" " , r " [TARGETDIR]Python.exe " , " REGISTRY.def " ) ,
( " DisplayIcon " , - 1 ,
r " Software \ Microsoft \ Windows \ CurrentVersion \ Uninstall \ %s " % product_code ,
2009-06-28 06:32:39 -03:00
" DisplayIcon " , " [TARGETDIR]python.exe " , " REGISTRY " )
2004-08-22 10:34:34 -03:00
] )
# Shortcuts, see "Shortcut Table"
add_data ( db , " Directory " ,
[ ( " ProgramMenuFolder " , " TARGETDIR " , " . " ) ,
( " MenuDir " , " ProgramMenuFolder " , " PY %s %s | %s Python %s . %s " % ( major , minor , testprefix , major , minor ) ) ] )
add_data ( db , " RemoveFile " ,
[ ( " MenuDir " , " TARGETDIR " , None , " MenuDir " , 2 ) ] )
2004-09-01 11:51:06 -03:00
tcltkshortcuts = [ ]
if have_tcl :
tcltkshortcuts = [
2004-08-22 10:34:34 -03:00
( " IDLE " , " MenuDir " , " IDLE|IDLE (Python GUI) " , " pythonw.exe " ,
2004-12-22 08:55:44 -04:00
tcltk . id , r ' " [TARGETDIR]Lib \ idlelib \ idle.pyw " ' , None , None , " python_icon.exe " , 0 , None , " TARGETDIR " ) ,
2004-08-22 10:34:34 -03:00
( " PyDoc " , " MenuDir " , " MODDOCS|Module Docs " , " pythonw.exe " ,
2004-12-22 08:55:44 -04:00
tcltk . id , r ' " [TARGETDIR]Tools \ scripts \ pydocgui.pyw " ' , None , None , " python_icon.exe " , 0 , None , " TARGETDIR " ) ,
2004-09-01 11:51:06 -03:00
]
add_data ( db , " Shortcut " ,
tcltkshortcuts +
[ # Advertised shortcuts: targets are features, not files
2004-08-22 10:34:34 -03:00
( " Python " , " MenuDir " , " PYTHON|Python (command line) " , " python.exe " ,
default_feature . id , None , None , None , " python_icon.exe " , 2 , None , " TARGETDIR " ) ,
2005-03-14 20:39:40 -04:00
# Advertising the Manual breaks on (some?) Win98, and the shortcut lacks an
# icon first.
#("Manual", "MenuDir", "MANUAL|Python Manuals", "documentation",
# htmlfiles.id, None, None, None, None, None, None, None),
2004-08-22 10:34:34 -03:00
## Non-advertised shortcuts: must be associated with a registry component
2005-03-14 20:39:40 -04:00
( " Manual " , " MenuDir " , " MANUAL|Python Manuals " , " REGISTRY.doc " ,
2008-05-09 15:21:55 -03:00
" [# %s ] " % docfile , None ,
2005-03-14 20:39:40 -04:00
None , None , None , None , None , None ) ,
2004-08-22 10:34:34 -03:00
( " Uninstall " , " MenuDir " , " UNINST|Uninstall Python " , " REGISTRY " ,
SystemFolderName + " msiexec " , " /x %s " % product_code ,
None , None , None , None , None , None ) ,
] )
db . Commit ( )
2010-11-14 14:17:39 -04:00
def build_pdbzip ( ) :
pdbexclude = [ ' kill_python.pdb ' , ' make_buildinfo.pdb ' ,
' make_versioninfo.pdb ' ]
path = " python- %s %s -pdb.zip " % ( full_current_version , msilib . arch_ext )
pdbzip = zipfile . ZipFile ( path , ' w ' )
for f in glob . glob1 ( os . path . join ( srcdir , PCBUILD ) , " *.pdb " ) :
if f not in pdbexclude and not f . endswith ( ' _d.pdb ' ) :
pdbzip . write ( os . path . join ( srcdir , PCBUILD , f ) , f )
pdbzip . close ( )
2010-03-16 15:49:28 -03:00
db , msiname = build_database ( )
2004-08-22 10:34:34 -03:00
try :
add_features ( db )
add_ui ( db )
add_files ( db )
add_registry ( db )
remove_old_versions ( db )
db . Commit ( )
finally :
del db
2010-03-16 15:49:28 -03:00
# Merge CRT into MSI file. This requires the database to be closed.
mod_dir = os . path . join ( os . environ [ " ProgramFiles " ] , " Common Files " , " Merge Modules " )
if msilib . Win64 :
modules = [ " Microsoft_VC90_CRT_x86_x64.msm " , " policy_9_0_Microsoft_VC90_CRT_x86_x64.msm " ]
else :
modules = [ " Microsoft_VC90_CRT_x86.msm " , " policy_9_0_Microsoft_VC90_CRT_x86.msm " ]
for i , n in enumerate ( modules ) :
modules [ i ] = os . path . join ( mod_dir , n )
def merge ( msi , feature , rootdir , modules ) :
cab_and_filecount = [ ]
# Step 1: Merge databases, extract cabfiles
m = msilib . MakeMerge2 ( )
m . OpenLog ( " merge.log " )
m . OpenDatabase ( msi )
for module in modules :
print module
m . OpenModule ( module , 0 )
m . Merge ( feature , rootdir )
print " Errors: "
for e in m . Errors :
print e . Type , e . ModuleTable , e . DatabaseTable
print " Modkeys: " ,
for s in e . ModuleKeys : print s ,
print
print " DBKeys: " ,
for s in e . DatabaseKeys : print s ,
print
cabname = tempfile . mktemp ( suffix = " .cab " )
m . ExtractCAB ( cabname )
cab_and_filecount . append ( ( cabname , len ( m . ModuleFiles ) ) )
m . CloseModule ( )
m . CloseDatabase ( True )
m . CloseLog ( )
# Step 2: Add CAB files
i = msilib . MakeInstaller ( )
db = i . OpenDatabase ( msi , constants . msiOpenDatabaseModeTransact )
v = db . OpenView ( " SELECT LastSequence FROM Media " )
v . Execute ( None )
maxmedia = - 1
while 1 :
r = v . Fetch ( )
if not r : break
seq = r . IntegerData ( 1 )
if seq > maxmedia :
maxmedia = seq
print " Start of Media " , maxmedia
for cabname , count in cab_and_filecount :
stream = " merged %d " % maxmedia
msilib . add_data ( db , " Media " ,
[ ( maxmedia + 1 , maxmedia + count , None , " # " + stream , None , None ) ] )
msilib . add_stream ( db , stream , cabname )
os . unlink ( cabname )
maxmedia + = count
# The merge module sets ALLUSERS to 1 in the property table.
# This is undesired; delete that
v = db . OpenView ( " DELETE FROM Property WHERE Property= ' ALLUSERS ' " )
v . Execute ( None )
v . Close ( )
db . Commit ( )
merge ( msiname , " SharedCRT " , " TARGETDIR " , modules )
# certname (from config.py) should be (a substring of)
# the certificate subject, e.g. "Python Software Foundation"
if certname :
2013-08-03 15:24:00 -03:00
os . system ( ' signtool sign /n " %s " '
' /t http://timestamp.verisign.com/scripts/timestamp.dll '
' /d " Python %s " '
' %s ' % ( certname , full_current_version , msiname ) )
2010-11-14 14:17:39 -04:00
if pdbzip :
build_pdbzip ( )