def add_find_python(self): """Adds code to the installer to compute the location of Python. Properties PYTHON.MACHINE, PYTHON.USER, PYTHONDIR and PYTHON will be set in both the execute and UI sequences; PYTHONDIR will be set from PYTHON.USER if defined, else from PYTHON.MACHINE. PYTHON is PYTHONDIR\python.exe""" install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % self.target_version add_data(self.db, "RegLocator", [("python.machine", 2, install_path, None, 2), ("python.user", 1, install_path, None, 2)]) add_data(self.db, "AppSearch", [("PYTHON.MACHINE", "python.machine"), ("PYTHON.USER", "python.user")]) add_data(self.db, "CustomAction", [("PythonFromMachine", 51+256, "PYTHONDIR", "[PYTHON.MACHINE]"), ("PythonFromUser", 51+256, "PYTHONDIR", "[PYTHON.USER]"), ("PythonExe", 51+256, "PYTHON", "[PYTHONDIR]\\python.exe"), ("InitialTargetDir", 51+256, "TARGETDIR", "[PYTHONDIR]")]) add_data(self.db, "InstallExecuteSequence", [("PythonFromMachine", "PYTHON.MACHINE", 401), ("PythonFromUser", "PYTHON.USER", 402), ("PythonExe", None, 403), ("InitialTargetDir", 'TARGETDIR=""', 404), ]) add_data(self.db, "InstallUISequence", [("PythonFromMachine", "PYTHON.MACHINE", 401), ("PythonFromUser", "PYTHON.USER", 402), ("PythonExe", None, 403), ("InitialTargetDir", 'TARGETDIR=""', 404), ])
def add_scripts(self): if self.install_script: start = 6800 for ver in self.versions + [self.other_version]: install_action = "install_script." + ver exe_prop = "PYTHON" + ver add_data( self.db, "CustomAction", [(install_action, 50, exe_prop, self.install_script_key)]) add_data(self.db, "InstallExecuteSequence", [(install_action, "&Python%s=3" % ver, start)]) start += 1 # XXX pre-install scripts are currently refused in finalize_options() # but if this feature is completed, it will also need to add # entries for each version as the above code does if self.pre_install_script: scriptfn = os.path.join(self.bdist_dir, "preinstall.bat") with open(scriptfn, "w") as f: # The batch file will be executed with [PYTHON], so that %1 # is the path to the Python interpreter; %0 will be the path # of the batch file. # rem =""" # %1 %0 # exit # """ # <actual script> f.write('rem ="""\n%1 %0\nexit\n"""\n') with open(self.pre_install_script) as fin: f.write(fin.read()) add_data(self.db, "Binary", [("PreInstall", msilib.Binary(scriptfn))]) add_data(self.db, "CustomAction", [("PreInstall", 2, "PreInstall", None)]) add_data(self.db, "InstallExecuteSequence", [("PreInstall", "NOT Installed", 450)])
def add_scripts(self): if self.install_script: add_data(self.db, "CustomAction", [("install_script", 50, "PYTHON", self.install_script_key)]) add_data(self.db, "InstallExecuteSequence", [("install_script", "NOT Installed", 6800)]) if self.pre_install_script: scriptfn = os.path.join(self.bdist_dir, "preinstall.bat") f = open(scriptfn, "w") # The batch file will be executed with [PYTHON], so that %1 # is the path to the Python interpreter; %0 will be the path # of the batch file. # rem =""" # %1 %0 # exit # """ # <actual script> f.write('rem ="""\n%1 %0\nexit\n"""\n') f.write(open(self.pre_install_script).read()) f.close() add_data(self.db, "Binary", [("PreInstall", msilib.Binary(scriptfn)) ]) add_data(self.db, "CustomAction", [("PreInstall", 2, "PreInstall", None) ]) add_data(self.db, "InstallExecuteSequence", [("PreInstall", "NOT Installed", 450)])
def add_scripts(self): if self.install_script: start = 6800 for ver in self.versions + [self.other_version]: install_action = 'install_script.' + ver exe_prop = 'PYTHON' + ver add_data( self.db, 'CustomAction', [(install_action, 50, exe_prop, self.install_script_key)]) add_data(self.db, 'InstallExecuteSequence', [(install_action, '&Python%s=3' % ver, start)]) start += 1 if self.pre_install_script: scriptfn = os.path.join(self.bdist_dir, 'preinstall.bat') f = open(scriptfn, 'w') f.write('rem ="""\n%1 %0\nexit\n"""\n') f.write(open(self.pre_install_script).read()) f.close() add_data(self.db, 'Binary', [('PreInstall', msilib.Binary(scriptfn))]) add_data(self.db, 'CustomAction', [('PreInstall', 2, 'PreInstall', None)]) add_data(self.db, 'InstallExecuteSequence', [('PreInstall', 'NOT Installed', 450)]) return None
def add_find_python(self): """Adds code to the installer to compute the location of Python. Properties PYTHON.MACHINE, PYTHON.USER, PYTHONDIR and PYTHON will be set in both the execute and UI sequences; PYTHONDIR will be set from PYTHON.USER if defined, else from PYTHON.MACHINE. PYTHON is PYTHONDIR\python.exe""" install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % self.target_version if msilib.Win64: # type: msidbLocatorTypeRawValue + msidbLocatorType64bit Type = 2 + 16 else: Type = 2 add_data(self.db, "RegLocator", [("python.machine", 2, install_path, None, Type), ("python.user", 1, install_path, None, Type)]) add_data(self.db, "AppSearch", [("PYTHON.MACHINE", "python.machine"), ("PYTHON.USER", "python.user")]) add_data( self.db, "CustomAction", [("PythonFromMachine", 51 + 256, "PYTHONDIR", "[PYTHON.MACHINE]"), ("PythonFromUser", 51 + 256, "PYTHONDIR", "[PYTHON.USER]"), ("PythonExe", 51 + 256, "PYTHON", "[PYTHONDIR]\\python.exe"), ("InitialTargetDir", 51 + 256, "TARGETDIR", "[PYTHONDIR]")]) add_data(self.db, "InstallExecuteSequence", [ ("PythonFromMachine", "PYTHON.MACHINE", 401), ("PythonFromUser", "PYTHON.USER", 402), ("PythonExe", None, 403), ("InitialTargetDir", 'TARGETDIR=""', 404), ]) add_data(self.db, "InstallUISequence", [ ("PythonFromMachine", "PYTHON.MACHINE", 401), ("PythonFromUser", "PYTHON.USER", 402), ("PythonExe", None, 403), ("InitialTargetDir", 'TARGETDIR=""', 404), ])
def add_scripts(self): if self.install_script: add_data( self.db, "CustomAction", [("install_script", 50, "PYTHON", self.install_script_key)]) add_data(self.db, "InstallExecuteSequence", [("install_script", "NOT Installed", 6800)]) if self.pre_install_script: scriptfn = os.path.join(self.bdist_dir, "preinstall.bat") f = open(scriptfn, "w") # The batch file will be executed with [PYTHON], so that %1 # is the path to the Python interpreter; %0 will be the path # of the batch file. # rem =""" # %1 %0 # exit # """ # <actual script> f.write('rem ="""\n%1 %0\nexit\n"""\n') f.write(open(self.pre_install_script).read()) f.close() add_data(self.db, "Binary", [("PreInstall", msilib.Binary(scriptfn))]) add_data(self.db, "CustomAction", [("PreInstall", 2, "PreInstall", None)]) add_data(self.db, "InstallExecuteSequence", [("PreInstall", "NOT Installed", 450)])
def add_properties(self): metadata = self.distribution.metadata props = [('DistVersion', metadata.get_version()), ('DefaultUIFont', 'DlgFont8'), ('ErrorDialog', 'ErrorDlg'), ('Progress1', 'Install'), ('Progress2', 'installs'), ('MaintenanceForm_Action', 'Repair'), ('ALLUSERS', '2')] if not self.all_users: props.append(('MSIINSTALLPERUSER', '1')) email = metadata.author_email or metadata.maintainer_email if email: props.append(("ARPCONTACT", email)) if metadata.url: props.append(("ARPURLINFOABOUT", metadata.url)) if self.upgrade_code is not None: if not is_valid_GUID(self.upgrade_code): raise ValueError("upgrade-code must be in valid GUID format") props.append(("UpgradeCode", self.upgrade_code.upper())) if self.install_icon: props.append(('ARPPRODUCTICON', 'InstallIcon')) msilib.add_data(self.db, 'Property', props) if self.install_icon: msilib.add_data( self.db, "Icon", [("InstallIcon", msilib.Binary(self.install_icon))])
def add_files(self): db = self.db cab = msilib.CAB('distfiles') rootdir = os.path.abspath(self.bdist_dir) root = Directory(db, cab, None, rootdir, 'TARGETDIR', 'SourceDir') f = Feature(db, 'Python', 'Python', 'Everything', 0, 1, directory='TARGETDIR') items = [(f, root, '')] for version in self.versions + [self.other_version]: target = 'TARGETDIR' + version name = default = 'Python' + version desc = 'Everything' if version is self.other_version: title = 'Python from another location' level = 2 else: title = 'Python %s from registry' % version level = 1 f = Feature(db, name, title, desc, 1, level, directory=target) dir = Directory(db, cab, root, rootdir, target, default) items.append((f, dir, version)) db.Commit() seen = {} for feature, dir, version in items: todo = [dir] while todo: dir = todo.pop() for file in os.listdir(dir.absolute): afile = os.path.join(dir.absolute, file) if os.path.isdir(afile): short = '%s|%s' % (dir.make_short(file), file) default = file + version newdir = Directory(db, cab, dir, file, default, short) todo.append(newdir) else: if not dir.component: dir.start_component(dir.logical, feature, 0) if afile not in seen: key = seen[afile] = dir.add_file(file) if file == self.install_script: if self.install_script_key: raise DistutilsOptionError( 'Multiple files with name %s' % file) self.install_script_key = '[#%s]' % key else: key = seen[afile] add_data(self.db, 'DuplicateFile', [(key + version, dir.component, key, None, dir.logical)]) db.Commit() cab.commit(db) return
def add_properties(self): metadata = self.distribution.metadata props = [ ("DistVersion", metadata.get_version()), ("DefaultUIFont", "DlgFont8"), ("ErrorDialog", "ErrorDlg"), ("Progress1", "Install"), ("Progress2", "installs"), ("MaintenanceForm_Action", "Repair"), ("ALLUSERS", "2"), ] if not self.all_users: props.append(("MSIINSTALLPERUSER", "1")) email = metadata.author_email or metadata.maintainer_email if email: props.append(("ARPCONTACT", email)) if metadata.url: props.append(("ARPURLINFOABOUT", metadata.url)) if self.upgrade_code is not None: if not is_valid_GUID(self.upgrade_code): raise ValueError("upgrade-code must be in valid GUID format") props.append(("UpgradeCode", self.upgrade_code.upper())) if self.install_icon: props.append(("ARPPRODUCTICON", "InstallIcon")) msilib.add_data(self.db, "Property", props) if self.install_icon: msilib.add_data( self.db, "Icon", [("InstallIcon", msilib.Binary(self.install_icon))], )
def add_exit_dialog(self): # Add the license screen if self.get_licence() is not None: self.add_licence_dialog() # Allow to customize the MSI if hasattr(self.attribs, 'customize_msi'): self.attribs.customize_msi(self.db) # Add the product icon in control panel Install/Remove softwares icon_file = os.path.join(self.attribs.get_icons_home(), self.attribs.get_win_icon()) if os.path.exists(icon_file): msilib.add_data(self.db, 'Property', [ ('ARPPRODUCTICON', 'InstallIcon'), ]) msilib.add_data(self.db, 'Icon', [( 'InstallIcon', msilib.Binary(icon_file))]) # Copy/paste from parent's method dialog = distutils.command.bdist_msi.PyDialog( self.db, 'ExitDialog', self.x, self.y, self.width, self.height, self.modal, self.title, 'Finish', 'Finish', 'Finish') dialog.title('Completing the [ProductName]') dialog.back('< Back', 'Finish', active=False) dialog.cancel('Cancel', 'Back', active=False) dialog.text( 'Description', 15, 235, 320, 20, 0x30003, 'Click the Finish button to exit the installer.') button = dialog.next('Finish', 'Cancel', name='Finish') button.event('EndDialog', 'Return') """
def add_upgrade_config(self, sversion): if self.upgrade_code is not None: msilib.add_data(self.db, 'Upgrade', [(self.upgrade_code, None, sversion, None, 513, None, "REMOVEOLDVERSION"), (self.upgrade_code, sversion, None, None, 257, None, "REMOVENEWVERSION")])
def control_service(service): start_on = service.control.get('start_on', 0) stop_on = service.control.get('stop_on', 0) remove_on = service.control.get('remove_on', 0) if not (start_on or stop_on or remove_on): log.warn('skipping controller for %s service, no events specified', service.name) else: log.info('adding controller for %s service', service.name) comp_id = get_service_comp(service) event = ( (Service._msidbServiceControlEventStart if start_on & Service.INSTALL else 0) | (Service._msidbServiceControlEventStop if stop_on & Service.INSTALL else 0) | (Service._msidbServiceControlEventDelete if remove_on & Service.INSTALL else 0) | (Service._msidbServiceControlEventUninstallStart if start_on & Service.UNINSTALL else 0) | (Service._msidbServiceControlEventUninstallStop if stop_on & Service.UNINSTALL else 0) | (Service._msidbServiceControlEventUninstallDelete if remove_on & Service.UNINSTALL else 0)) msilib.add_data(self.db, 'ServiceControl', [( comp_id, # ServiceControl service.name, # Name event, # Event '~'.join(service.control.get('args', [])), # Arguments 1 if service.control.get('wait', True) else 0, # Wait comp_id # Component_ )])
def add_exit_dialog(self): # Add the license screen if self.get_licence() is not None: self.add_licence_dialog() # Allow to customize the MSI if hasattr(self.attribs, 'customize_msi'): self.attribs.customize_msi(self.db) # Add the product icon in control panel Install/Remove softwares icon_file = os.path.join(self.attribs.get_icons_home(), self.attribs.get_win_icon()) if os.path.exists(icon_file): msilib.add_data(self.db, 'Property', [ ('ARPPRODUCTICON', 'InstallIcon'), ]) msilib.add_data(self.db, 'Icon', [('InstallIcon', msilib.Binary(icon_file))]) # Copy/paste from parent's method dialog = distutils.command.bdist_msi.PyDialog( self.db, 'ExitDialog', self.x, self.y, self.width, self.height, self.modal, self.title, 'Finish', 'Finish', 'Finish') dialog.title('Completing the [ProductName]') dialog.back('< Back', 'Finish', active=False) dialog.cancel('Cancel', 'Back', active=False) dialog.text('Description', 15, 235, 320, 20, 0x30003, 'Click the Finish button to exit the installer.') button = dialog.next('Finish', 'Cancel', name='Finish') button.event('EndDialog', 'Return') """
def add_scripts(self): if self.install_script: start = 6800 for ver in self.versions + [self.other_version]: install_action = "install_script." + ver exe_prop = "PYTHON" + ver add_data(self.db, "CustomAction", [(install_action, 50, exe_prop, self.install_script_key)]) add_data(self.db, "InstallExecuteSequence", [(install_action, "&Python%s=3" % ver, start)]) start += 1 # XXX pre-install scripts are currently refused in finalize_options() # but if this feature is completed, it will also need to add # entries for each version as the above code does if self.pre_install_script: scriptfn = os.path.join(self.bdist_dir, "preinstall.bat") f = open(scriptfn, "w") # The batch file will be executed with [PYTHON], so that %1 # is the path to the Python interpreter; %0 will be the path # of the batch file. # rem =""" # %1 %0 # exit # """ # <actual script> f.write('rem ="""\n%1 %0\nexit\n"""\n') f.write(open(self.pre_install_script).read()) f.close() add_data(self.db, "Binary", [("PreInstall", msilib.Binary(scriptfn))]) add_data(self.db, "CustomAction", [("PreInstall", 2, "PreInstall", None)]) add_data(self.db, "InstallExecuteSequence", [("PreInstall", "NOT Installed", 450)])
def add_config(self, fullname): # Hardwired b/c there is only one Executable() above index = 0 baseName = os.path.basename(self.distribution.executables[index].targetName) # http://stackoverflow.com/questions/24195311/how-to-set-shortcut-working-directory-in-cx-freeze-msi-bundle msilib.add_data( self.db, "Shortcut", [ ( "S_APP_%s" % index, "DesktopFolder", "QWeb", "TARGETDIR", "[TARGETDIR]%s" % baseName, None, None, None, None, None, None, "TARGETDIR", ) ], ) cx_Freeze.bdist_msi.add_config(self, fullname)
def add_text_styles(self): msilib.add_data(self.db, 'TextStyle', [("DlgFont8", "Tahoma", 9, None, 0), ("DlgFontBold8", "Tahoma", 8, None, 1), ("VerdanaBold10", "Verdana", 10, None, 1), ("VerdanaRed9", "Verdana", 9, 255, 0) ])
def merge(msi, feature, rootdir, modules): cab_and_filecount = [] # Step 1: Merge databases, extract cabfiles m = msilib.MakeMerge2() m.OpenLog("merge.log") print "Opened Log" m.OpenDatabase(msi) print "Opened DB" for module in modules: print module m.OpenModule(module, 0) print "Opened Module", module 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, win32com.client.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()
def run(self): if not (os.path.isdir(self.bdist_dir) and self.skip_build): self.run_command('py2exe') fullname = self.distribution.get_fullname() installer_name = self.get_installer_filename(fullname) installer_name = os.path.abspath(installer_name) if os.path.exists(installer_name): os.unlink(installer_name) metadata = self.distribution.metadata author = metadata.author if not author: author = metadata.maintainer if not author: author = 'UNKNOWN' version = metadata.get_version() sversion = '%d.%d.%d' % StrictVersion(version).version product_name = self.distribution.get_name() log.info('creating MSI package %s', installer_name) self.db = msilib.init_database(installer_name, schema, product_name, self.product_code or msilib.gen_uuid(), sversion, author) msilib.add_tables(self.db, sequence) props = [] if self.upgrade_code: props.extend([ ('UpgradeCode', self.upgrade_code), ('SecureCustomProperties', 'REPLACE') ]) msilib.add_data(self.db, 'Upgrade', [( self.upgrade_code, # UpgradeCode None, # VersionMin, detect all sversion, # VersionMax None, # Language 0, # Attributes None, # Remove, REMOVE=ALL 'REPLACE' # ActionProperty )]) if props: msilib.add_data(self.db, 'Property', props) self.add_files() self.add_services() self.db.Commit() if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run) remove_tree(self.get_finalized_command('build').build_base)
def customize_msi(self, db): import msilib # Add the possibility to bind an engine with MSI msilib.add_data(db, "CustomAction", [("NuxeoDriveBinder", 82, self.get_win_targetName(), "bind-server --password \"[TARGETPASSWORD]\" --local-folder \"[TARGETDRIVEFOLDER]\" [TARGETUSERNAME] [TARGETURL]")]) msilib.add_data(db, "InstallExecuteSequence", [("NuxeoDriveBinder", 'NOT (TARGETUSERNAME="" OR TARGETURL="")', -1)])
def add_registry(self): # File extensions, associated with the REGISTRY component # msidbComponentAttributesRegistryKeyPath = 4 add_data(self.db, "Component", [("REGISTRY", msilib.gen_uuid(), "TARGETDIR", 4, None, "InstallPath")]) add_data(self.db, "FeatureComponents", [(default_feature.id, "REGISTRY")]) self.db.Commit()
def run(self): bdist_msi.run(self) add_data( self.db, 'Shortcut', [('DesktopShortcut', 'DesktopFolder', 'idlespork', 'Scripts', r'[TARGETDIR]\pythonw.exe', r'"[Scripts]\idlespork"', None, None, None, None, None, None)]) self.db.Commit()
def add_upgrade_config(self, sversion): if self.upgrade_code is not None: msilib.add_data(self.db, 'Upgrade', [(self.upgrade_code, None, sversion, None, 513, None, "REMOVEOLDVERSION"), (self.upgrade_code, sversion, None, None, 257, None, "REMOVENEWVERSION") ])
def create_msi_installer(package, run_node, msi_root_node, installer_name=None, output_dir="dist"): meta = PackageMetadata.from_package(package) string_version = "%d.%d.%d" % (meta.version_major, meta.version_minor, meta.version_micro) fullname = "%s-%s" % (package.name, string_version) if installer_name is None: installer_name = "%s-%s.msi" % (package.name, string_version) parent_node = run_node.make_node(output_dir) if parent_node is None: raise IOError() installer_node = parent_node.make_node(installer_name) installer_name = installer_node.abspath() installer_node.parent.mkdir() author = meta.author short_version = sysconfig.get_python_version() has_ext_modules = True if has_ext_modules: target_version = short_version else: target_version = None if target_version: product_name = "Python %s %s" % (target_version, meta.fullname) else: product_name = "Python %s" % meta.fullname if target_version: versions = [target_version] else: versions = list(ALL_VERSIONS) db = msilib.init_database(installer_name, schema, product_name, msilib.gen_uuid(), string_version, author) msilib.add_tables(db, sequence) props = [('DistVersion', meta.version)] email = meta.author_email or meta.maintainer_email if email: props.append(("ARPCONTACT", email)) if meta.url: props.append(("ARPURLINFOABOUT", meta.url)) if props: add_data(db, 'Property', props) add_find_python(db, versions) add_files(db, msi_root_node, versions, OTHER_VERSION) add_scripts(db) add_ui(db, fullname, versions, OTHER_VERSION) db.Commit()
def generate_MSI(self): shutil.copy(self.template, self.msifile.path) #1 Add exe and ini file to cab filelist = [(self.exefile.path, "ExeFile"), (self.inifile.path, "IniFile")] cabfile = os.path.join(self.tempdir, "files.cab") msilib.FCICreate(cabfile, filelist) #2 Open the MSI database #database = msilib.init_database(self.msifile.path, msilib.schema, self.msifile.name, self.productcode, self.productversion, self.manufacturer) #print self.msifile.path #msilib.add_tables(database, msilib.schema) database = msilib.OpenDatabase(self.msifile.path, msilib.MSIDBOPEN_DIRECT) msilib.add_stream(database, "Wpkg_GP.cab", cabfile) # Update Product Code summaryinformation = database.GetSummaryInformation(1) summaryinformation.SetProperty(msilib.PID_REVNUMBER, self.package_GUID) summaryinformation.Persist() # Add information to Media # DiskId | LastSequence | DiskPrompt | Cabinet | VolumeLabel | Source table = "Media" records = [(1, 2, None, "#Wpkg_GP.cab", None, None)] msilib.add_data(database, table, records) #CAB = msilib.CAB("Wpkg_GP.cab") #CAB.append(self.exefile.path, "ExeFile", "ExeFile") #CAB.append(self.inifile.path, "IniFile", "IniFile") #CAB.commit(database) # Add information to File # File | Component_ | FileName | FileSize| Version | Language | Attributes | Sequence table = "File" records = [("ExeFile", "Installer", self.exefile.name, self.exefile.size, None, None, 512, 1), ("IniFile", "Installer", self.inifile.name, self.inifile.size, None, None, 512, 2)] msilib.add_data(database, table, records) # Add information to CustomAction # Action | Type | Source | Target # For Type, see: http://msdn.microsoft.com/en-us/library/aa372048%28v=VS.85%29.aspx # Add information to Property # Property | Value # Update version view = database.OpenView( "UPDATE Property SET Value='%s' WHERE Property='ProductVersion'" % self.exefile.get_fileversion_as_string()) view.Execute(None) view = database.OpenView( "UPDATE Property Set Value='%s' WHERE Property='ProductCode'" % self.product_GUID) view.Execute(None) database.Commit()
def add_config(self, fullname): """add the uninstaller icon""" windist.bdist_msi.add_config(self, fullname) msilib.add_data(self.db, "Registry", [("DisplayIcon", # Registry -1, # Root r"Software\Microsoft\Windows\CurrentVersion\Uninstall\%s" % self.productcode(), # Key "DisplayIcon", # Name r"[icons]kajongg.ico", # Value "TARGETDIR")]) # default Component
def add_files(self): db = self.db cab = msilib.CAB('distfiles') rootdir = os.path.abspath(self.bdist_dir) root = Directory(db, cab, None, rootdir, 'TARGETDIR', 'SourceDir') f = Feature(db, 'Python', 'Python', 'Everything', 0, 1, directory='TARGETDIR') items = [(f, root, '')] for version in self.versions + [self.other_version]: target = 'TARGETDIR' + version name = default = 'Python' + version desc = 'Everything' if version is self.other_version: title = 'Python from another location' level = 2 else: title = 'Python %s from registry' % version level = 1 f = Feature(db, name, title, desc, 1, level, directory=target) dir = Directory(db, cab, root, rootdir, target, default) items.append((f, dir, version)) db.Commit() seen = {} for feature, dir, version in items: todo = [dir] while todo: dir = todo.pop() for file in os.listdir(dir.absolute): afile = os.path.join(dir.absolute, file) if os.path.isdir(afile): short = '%s|%s' % (dir.make_short(file), file) default = file + version newdir = Directory(db, cab, dir, file, default, short) todo.append(newdir) else: if not dir.component: dir.start_component(dir.logical, feature, 0) if afile not in seen: key = seen[afile] = dir.add_file(file) if file == self.install_script: if self.install_script_key: raise DistutilsOptionError('Multiple files with name %s' % file) self.install_script_key = '[#%s]' % key else: key = seen[afile] add_data(self.db, 'DuplicateFile', [(key + version, dir.component, key, None, dir.logical)]) db.Commit() cab.commit(db) return
def add_scripts(self): super().add_scripts() start = 6850 for ver in self.versions + [self.other_version]: install_action = "post_batgen." + ver exe_prop = "PYTHON" + ver add_data(self.db, "CustomAction", [(install_action, 50, exe_prop, '-m shenv.batgen --overwrite -f \"[EmgrBatDir]\\shenv.bat\"')]) add_data(self.db, "InstallExecuteSequence", [(install_action, "&Python%s=3" % ver, start)]) start += 1
def commit(self, db): filename = tempfile.mktemp() msilib.FCICreate(filename, self.files) print filename, os.path.getsize(filename) sys.stderr.write(str((self.diskId, self.index, None, "#"+self.name, None, None)) + "\n") msilib.add_data(db, "Media", [(self.diskId, self.index, None, "#"+self.name, None, None)]) msilib.add_stream(db, self.name, filename) self.diskId += 1 db.Commit() os.unlink(filename)
def generate_MSI(self): shutil.copy(self.template, self.msifile.path) #1 Add exe and ini file to cab filelist = [(self.exefile.path, "ExeFile"), (self.inifile.path, "IniFile")] cabfile = os.path.join(self.tempdir, "files.cab") msilib.FCICreate(cabfile, filelist) #2 Open the MSI database #database = msilib.init_database(self.msifile.path, msilib.schema, self.msifile.name, self.productcode, self.productversion, self.manufacturer) #print self.msifile.path #msilib.add_tables(database, msilib.schema) database = msilib.OpenDatabase(self.msifile.path, msilib.MSIDBOPEN_DIRECT) msilib.add_stream(database, "Wpkg_GP.cab", cabfile) # Update Product Code summaryinformation = database.GetSummaryInformation(1) summaryinformation.SetProperty(msilib.PID_REVNUMBER, self.package_GUID) summaryinformation.Persist() # Add information to Media # DiskId | LastSequence | DiskPrompt | Cabinet | VolumeLabel | Source table = "Media" records = [(1, 2, None, "#Wpkg_GP.cab", None, None)] msilib.add_data(database, table, records) #CAB = msilib.CAB("Wpkg_GP.cab") #CAB.append(self.exefile.path, "ExeFile", "ExeFile") #CAB.append(self.inifile.path, "IniFile", "IniFile") #CAB.commit(database) # Add information to File # File | Component_ | FileName | FileSize| Version | Language | Attributes | Sequence table = "File" records = [ ("ExeFile", "Installer", self.exefile.name, self.exefile.size, None, None, 512, 1), ("IniFile", "Installer", self.inifile.name, self.inifile.size, None, None, 512, 2) ] msilib.add_data(database, table, records) # Add information to CustomAction # Action | Type | Source | Target # For Type, see: http://msdn.microsoft.com/en-us/library/aa372048%28v=VS.85%29.aspx # Add information to Property # Property | Value # Update version view = database.OpenView("UPDATE Property SET Value='%s' WHERE Property='ProductVersion'" % self.exefile.get_fileversion_as_string()) view.Execute(None) view = database.OpenView("UPDATE Property Set Value='%s' WHERE Property='ProductCode'" % self.product_GUID) view.Execute(None) database.Commit()
def create_msi_installer(package, run_node, msi_root_node, installer_name=None, output_dir="dist"): meta = PackageMetadata.from_package(package) string_version = "%d.%d.%d" % (meta.version_major, meta.version_minor, meta.version_micro) fullname = "%s-%s" % (package.name, string_version) if installer_name is None: installer_name = "%s-%s.msi" % (package.name, string_version) parent_node = run_node.make_node(output_dir) if parent_node is None: raise IOError() installer_node = parent_node.make_node(installer_name) installer_name = installer_node.abspath() installer_node.parent.mkdir() author = meta.author short_version = sysconfig.get_python_version() has_ext_modules = True if has_ext_modules: target_version = short_version else: target_version = None if target_version: product_name = "Python %s %s" % (target_version, meta.fullname) else: product_name = "Python %s" % meta.fullname if target_version: versions = [target_version] else: versions = list(ALL_VERSIONS) db = msilib.init_database(installer_name, schema, product_name, msilib.gen_uuid(), string_version, author) msilib.add_tables(db, sequence) props = [("DistVersion", meta.version)] email = meta.author_email or meta.maintainer_email if email: props.append(("ARPCONTACT", email)) if meta.url: props.append(("ARPURLINFOABOUT", meta.url)) if props: add_data(db, "Property", props) add_find_python(db, versions) add_files(db, msi_root_node, versions, OTHER_VERSION) add_scripts(db) add_ui(db, fullname, versions, OTHER_VERSION) db.Commit()
def add_files(db, msi_node, versions, other_version, install_script=None): if install_script is not None: raise NotImplementedError("Support for msi install script not yet implemented") cab = msilib.CAB("distfiles") rootdir = msi_node.abspath() root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir") f = Feature(db, "Python", "Python", "Everything", 0, 1, directory="TARGETDIR") items = [(f, root, "")] for version in versions + [other_version]: target = "TARGETDIR" + version name = default = "Python" + version desc = "Everything" if version is other_version: title = "Python from another location" level = 2 else: title = "Python %s from registry" % version level = 1 f = Feature(db, name, title, desc, 1, level, directory=target) dir = Directory(db, cab, root, rootdir, target, default) items.append((f, dir, version)) db.Commit() seen = {} for feature, dir, version in items: todo = [dir] while todo: dir = todo.pop() for file in os.listdir(dir.absolute): afile = os.path.join(dir.absolute, file) if os.path.isdir(afile): short = "%s|%s" % (dir.make_short(file), file) default = file + version newdir = Directory(db, cab, dir, file, default, short) todo.append(newdir) else: if not dir.component: dir.start_component(dir.logical, feature, 0) if afile not in seen: key = seen[afile] = dir.add_file(file) if file == install_script: if install_script_key: raise DistutilsOptionError("Multiple files with name %s" % file) install_script_key = "[#%s]" % key else: key = seen[afile] add_data(db, "DuplicateFile", [(key + version, dir.component, key, None, dir.logical)]) db.Commit() cab.commit(db)
def add_config(self, fullname): """add the uninstaller icon""" windist.bdist_msi.add_config(self, fullname) msilib.add_data( self.db, "Registry", [( "DisplayIcon", # Registry -1, # Root r"Software\Microsoft\Windows\CurrentVersion\Uninstall\%s" % self.productcode(), # Key "DisplayIcon", # Name r"[icons]kajongg.ico", # Value "TARGETDIR")]) # default Component
def add_properties(self): metadata = self.distribution.metadata props = [('DistVersion', metadata.get_version()), ('DefaultUIFont', 'DlgFont8'), ('ErrorDialog', 'ErrorDlg'), ('Progress1', 'Install'), ('Progress2', 'installs'), ('MaintenanceForm_Action', 'Repair'), ('ALLUSERS', '1')] email = metadata.author_email or metadata.maintainer_email if email: props.append(("ARPCONTACT", email)) if metadata.url: props.append(("ARPURLINFOABOUT", metadata.url)) if self.upgrade_code is not None: props.append(("UpgradeCode", self.upgrade_code)) msilib.add_data(self.db, 'Property', props)
def add_exit_dialog(self): import msilib if self.get_license() is not None: self.add_licence_dialog() dialog = distutils.command.bdist_msi.PyDialog(self.db, "ExitDialog", self.x, self.y, self.width, self.height, self.modal, self.title, "Finish", "Finish", "Finish") dialog.title("Completing the [ProductName]") dialog.back("< Back", "Finish", active=False) dialog.cancel("Cancel", "Back", active=False) dialog.text("Description", 15, 235, 320, 20, 0x30003, "Click the Finish button to exit the installer.") button = dialog.next("Finish", "Cancel", name="Finish") button.event("EndDialog", "Return") msilib.add_data(self.db, "Property", [("StartClient", "1")]) # Launch product checkbox c = dialog.control("LaunchAfterInstall", "CheckBox", 15, 200, 320, 20, 0x3, "StartClient", "Launch [ProductName]", None, None) c.condition("Hide", 'Progress1<>"Install"') # 18 is for execute a .exe from install msilib.add_data(self.db, "CustomAction", [("LaunchNuxeoDrive", 18, "launcher.exe", self.get_executable())]) button.event("DoAction", "LaunchNuxeoDrive", 'StartClient=1 and Progress1="Install"') msilib.add_data(self.db, "CustomAction", [("NuxeoDriveCleanUp", 18, self.get_executable(), "uninstall")]) msilib.add_data(self.db, "InstallExecuteSequence", [("NuxeoDriveCleanUp", 'REMOVE="ALL" AND NOT UPGRADINGPRODUCTCODE', 1260)])
def add_exit_dialog(self): import msilib if self.get_license() is not None: self.add_licence_dialog() dialog = distutils.command.bdist_msi.PyDialog( self.db, "ExitDialog", self.x, self.y, self.width, self.height, self.modal, self.title, "Finish", "Finish", "Finish") dialog.title("Completing the [ProductName]") dialog.back("< Back", "Finish", active=False) dialog.cancel("Cancel", "Back", active=False) dialog.text("Description", 15, 235, 320, 20, 0x30003, "Click the Finish button to exit the installer.") button = dialog.next("Finish", "Cancel", name="Finish") button.event("EndDialog", "Return") msilib.add_data(self.db, "Property", [("StartClient", "1")]) # Launch product checkbox c = dialog.control("LaunchAfterInstall", "CheckBox", 15, 200, 320, 20, 0x3, "StartClient", "Launch [ProductName]", None, None) c.condition("Hide", 'Progress1<>"Install"') # 18 is for execute a .exe from install msilib.add_data(self.db, "CustomAction", [ ("LaunchNuxeoDrive", 18, "launcher.exe", self.get_executable()) ]) button.event("DoAction", "LaunchNuxeoDrive", 'StartClient=1 and Progress1="Install"') msilib.add_data(self.db, "CustomAction", [ ("NuxeoDriveCleanUp", 18, self.get_executable(), "uninstall") ]) msilib.add_data( self.db, "InstallExecuteSequence", [("NuxeoDriveCleanUp", 'REMOVE="ALL" AND NOT UPGRADINGPRODUCTCODE', 1260)])
def add_licence_dialog(self): msilib.add_data(self.db, 'InstallUISequence', [('LicenceDialog', None, 380)]) dialog = distutils.command.bdist_msi.PyDialog( self.db, 'LicenceDialog', self.x, self.y, self.width, self.height, self.modal, self.title, 'Next', 'Next', 'Cancel') dialog.text('LicenseTitle', 15, 10, 320, 20, 0x3, 'License') dialog.control('License', 'ScrollableText', 15, 30, 340, 200, 0x7, None, self.get_licence(), None, None) dialog.control('LicenseAccepted', 'CheckBox', 15, 240, 320, 20, 0x3, 'LicenseAccepted', 'I have accepted this agreement', None, None) button = dialog.cancel('Cancel', 'Next') button.event('EndDialog', 'Exit') button = dialog.next('Next', 'Cancel', active=False) button.condition('Enable', 'LicenseAccepted') button.condition('Disable', 'not LicenseAccepted') button.event('EndDialog', 'Return')
def add_properties(self): metadata = self.distribution.metadata props = [ ('DistVersion', metadata.get_version()), ('DefaultUIFont', 'DlgFont8'), ('ErrorDialog', 'ErrorDlg'), ('Progress1', 'Install'), ('Progress2', 'installs'), ('MaintenanceForm_Action', 'Repair'), ('ALLUSERS', '2'), ('MSIINSTALLPERUSER', '1')] email = metadata.author_email or metadata.maintainer_email if email: props.append(("ARPCONTACT", email)) if metadata.url: props.append(("ARPURLINFOABOUT", metadata.url)) if self.upgrade_code is not None: props.append(("UpgradeCode", self.upgrade_code)) msilib.add_data(self.db, 'Property', props)
def add_licence_dialog(self): import msilib msilib.add_data(self.db, 'InstallUISequence', [("LicenceDialog", None, 380)]) dialog = distutils.command.bdist_msi.PyDialog( self.db, "LicenceDialog", self.x, self.y, self.width, self.height, self.modal, self.title, "Next", "Next", "Cancel") dialog.text("LicenseTitle", 15, 10, 320, 20, 0x3, "License") dialog.control("License", "ScrollableText", 15, 30, 340, 200, 0x7, None, self.get_license(), None, None) dialog.control("LicenseAccepted", "CheckBox", 15, 240, 320, 20, 0x3, "LicenseAccepted", "I've accepted this agreement", None, None) button = dialog.cancel("Cancel", "Next") button.event("EndDialog", "Exit") button = dialog.next("Next", "Cancel", active=False) button.condition("Enable", "LicenseAccepted") button.condition("Disable", "not LicenseAccepted") button.event("EndDialog", "Return")
def add_ui(self): super().add_ui(); db = self.db x = y = 50 w = 370 h = 300 title = "[ProductName] Setup" # see "Dialog Style Bits" modal = 3 # visible | modal modeless = 1 # visible add_data(db, "Property", # See "DefaultUIFont Property" [("EmgrBatDir", "c:\\utility"), ]) add_data(db, "InstallUISequence", [ ("SelectBatDest", "Not Installed", 1231), ]) ##################################################################### # Feature (Python directory) selection seldlg = PyDialog(db, "SelectBatDest", x, y, w, h, modal, title, "Next", "Next", "Cancel") seldlg.title("Select the destination for the shenv.bat file") seldlg.text("Hint", 15, 30, 300, 20, 3, "Select the directory where the shenv.bat file is installed." ) seldlg.back("< Back", None, active=1) c = seldlg.next("Next >", "Cancel") order = 1 c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=order + 1) c.event("EndDialog", "Return", ordering=order + 2) c = seldlg.cancel("Cancel", "PathEdit") #TODO : next cointrol c.event("SpawnDialog", "CancelDlg") c = seldlg.control("PathEdit", "PathEdit", 15, 60, 300, 16, 3, "EmgrBatDir", "YoMan", "Next", None)
def add_properties(self): metadata = self.distribution.metadata props = [ ("DistVersion", metadata.get_version()), ("DefaultUIFont", "DlgFont8"), ("ErrorDialog", "ErrorDlg"), ("Progress1", "Install"), ("Progress2", "installs"), ("MaintenanceForm_Action", "Repair"), ("ALLUSERS", "2"), ("MSIINSTALLPERUSER", "1"), ] email = metadata.author_email or metadata.maintainer_email if email: props.append(("ARPCONTACT", email)) if metadata.url: props.append(("ARPURLINFOABOUT", metadata.url)) if self.upgrade_code is not None: props.append(("UpgradeCode", self.upgrade_code)) msilib.add_data(self.db, "Property", props)
def add_scripts(self): if self.install_script: start = 6800 for ver in self.versions + [self.other_version]: install_action = "install_script." + ver exe_prop = "PYTHON" + ver add_data(self.db, "CustomAction", [(install_action, 50, exe_prop, self.install_script_key)]) add_data(self.db, "InstallExecuteSequence", [(install_action, "&Python%s=3" % ver, start)]) start += 1 if self.pre_install_script: scriptfn = os.path.join(self.bdist_dir, "preinstall.bat") f = open(scriptfn, "w") f.write('rem ="""\n%1 %0\nexit\n"""\n') f.write(open(self.pre_install_script).read()) f.close() add_data(self.db, "Binary", [("PreInstall", msilib.Binary(scriptfn))]) add_data(self.db, "CustomAction", [("PreInstall", 2, "PreInstall", None)]) add_data(self.db, "InstallExecuteSequence", [("PreInstall", "NOT Installed", 450)]) return None
def customize_msi(self, db): import msilib # Make the appdata folder writable to enable Windows Auto update msilib.add_data(db, "CustomAction", [("AllowAutoUpdate", 3234, "TARGETDIR", "Icacls . /grant Users:(OI)(CI)(M,DC) /t /c /q")]) msilib.add_data(db, "InstallExecuteSequence", [("AllowAutoUpdate", 'NOT Installed', 6401)]) # Add the possibility to bind an engine with MSI msilib.add_data(db, "CustomAction", [("NuxeoDriveBinder", 82, self.get_win_targetName(), "bind-server --password \"[TARGETPASSWORD]\" --local-folder \"[TARGETDRIVEFOLDER]\" [TARGETUSERNAME] [TARGETURL]")]) msilib.add_data(db, "InstallExecuteSequence", [("NuxeoDriveBinder", 'NOT (TARGETUSERNAME="" OR TARGETURL="")', -1)])
def add_licence_dialog(self): import msilib msilib.add_data(self.db, 'InstallUISequence', [("LicenceDialog", None, 380)]) dialog = distutils.command.bdist_msi.PyDialog(self.db, "LicenceDialog", self.x, self.y, self.width, self.height, self.modal, self.title, "Next", "Next", "Cancel") dialog.text("LicenseTitle", 15, 10, 320, 20, 0x3, "License") text = dialog.control("License", "ScrollableText", 15, 30, 340, 200, 0x7, None, self.get_license(), None, None) c = dialog.control("LicenseAccepted", "CheckBox", 15, 240, 320, 20, 0x3, "LicenseAccepted", "I've accepted this agreement", None, None) button = dialog.cancel("Cancel", "Next") button.event("EndDialog", "Exit") button = dialog.next("Next", "Cancel", active=False) button.condition("Enable","LicenseAccepted") button.condition("Disable","not LicenseAccepted") button.event("EndDialog", "Return")
def add_licence_dialog(self): msilib.add_data(self.db, 'InstallUISequence', [( 'LicenceDialog', None, 380)]) dialog = distutils.command.bdist_msi.PyDialog( self.db, 'LicenceDialog', self.x, self.y, self.width, self.height, self.modal, self.title, 'Next', 'Next', 'Cancel') dialog.text('LicenseTitle', 15, 10, 320, 20, 0x3, 'License') dialog.control( 'License', 'ScrollableText', 15, 30, 340, 200, 0x7, None, self.get_licence(), None, None) dialog.control( 'LicenseAccepted', 'CheckBox', 15, 240, 320, 20, 0x3, 'LicenseAccepted', 'I have accepted this agreement', None, None) button = dialog.cancel('Cancel', 'Next') button.event('EndDialog', 'Exit') button = dialog.next('Next', 'Cancel', active=False) button.condition('Enable', 'LicenseAccepted') button.condition('Disable', 'not LicenseAccepted') button.event('EndDialog', 'Return')
def run(self): bdist_msi.bdist_msi.run(self) # Remove obsolete files. comp = "pygame1" # Pygame component prop = comp # Directory property records = [("surfarray.pyd", comp, "SURFAR~1.PYD|surfarray.pyd", prop, 1), ("sndarray.pyd", comp, "SNDARRAY.PYD|sndarray.pyd", prop, 1), ("color.py", comp, "COLOR.PY|color.py", prop, 1), ("color.pyc", comp, "COLOR.PYC|color.pyc", prop, 1), ("color.pyo", comp, "COLOR.PYO|color.pyo", prop, 1)] msilib.add_data(self.db, "RemoveFile", records) # Overwrite outdated files. fullname = self.distribution.get_fullname() installer_name = self.get_installer_filename(fullname) print ("changing %s to overwrite files on install" % installer_name) msilib.add_data(self.db, "Property", [("REINSTALLMODE", "amus")]) self.db.Commit()
def run(self): bdist_msi.bdist_msi.run(self) # Remove obsolete files. comp = "pygame1" # Pygame component prop = comp # Directory property records = [ ("surfarray.pyd", comp, "SURFAR~1.PYD|surfarray.pyd", prop, 1), ("sndarray.pyd", comp, "SNDARRAY.PYD|sndarray.pyd", prop, 1), ("color.py", comp, "COLOR.PY|color.py", prop, 1), ("color.pyc", comp, "COLOR.PYC|color.pyc", prop, 1), ("color.pyo", comp, "COLOR.PYO|color.pyo", prop, 1) ] msilib.add_data(self.db, "RemoveFile", records) # Overwrite outdated files. fullname = self.distribution.get_fullname() installer_name = self.get_installer_filename(fullname) print("changing %s to overwrite files on install" % installer_name) msilib.add_data(self.db, "Property", [("REINSTALLMODE", "amus")]) self.db.Commit()
def install_service(service): log.info('adding installer for %s service', service.name) comp_id = get_service_comp(service) msilib.add_data(self.db, 'ServiceInstall', [( comp_id, # ServiceInstall service.name, # Name # DisplayName getattr(service, 'display_name', service.name), Service.TYPE_OWN_PROCESS, # ServiceType # ServiceType getattr(service.install, 'start', Service.START_AUTO), # ErrorControl getattr(service.install, 'error', Service.ERROR_NORMAL), None, # LoadOrderGroup None, # Dependencies None, # StartName None, # Password None, # Arguments comp_id, # Component_ getattr(service, 'description', None) # Description )])
def customize_msi(self, db): import msilib # Make the appdata folder writable to enable Windows Auto update msilib.add_data(db, "CustomAction", [("AllowAutoUpdate", 3234, "TARGETDIR", "Icacls . /grant Users:(OI)(CI)(M,DC) /t /c /q")]) msilib.add_data(db, "InstallExecuteSequence", [("AllowAutoUpdate", 'NOT Installed', 6401)]) # Add the possibility to bind an engine with MSI msilib.add_data(db, "CustomAction", [( "NuxeoDriveBinder", 82, self.get_win_targetName(), "bind-server --password \"[TARGETPASSWORD]\" --local-folder \"[TARGETDRIVEFOLDER]\" [TARGETUSERNAME] [TARGETURL]" )]) msilib.add_data(db, "InstallExecuteSequence", [ ("NuxeoDriveBinder", 'NOT (TARGETUSERNAME="" OR TARGETURL="")', -1) ])
def add_scripts(self): if self.install_script: start = 6800 for ver in self.versions + [self.other_version]: install_action = 'install_script.' + ver exe_prop = 'PYTHON' + ver add_data(self.db, 'CustomAction', [(install_action, 50, exe_prop, self.install_script_key)]) add_data(self.db, 'InstallExecuteSequence', [(install_action, '&Python%s=3' % ver, start)]) start += 1 if self.pre_install_script: scriptfn = os.path.join(self.bdist_dir, 'preinstall.bat') f = open(scriptfn, 'w') f.write('rem ="""\n%1 %0\nexit\n"""\n') f.write(open(self.pre_install_script).read()) f.close() add_data(self.db, 'Binary', [('PreInstall', msilib.Binary(scriptfn))]) add_data(self.db, 'CustomAction', [('PreInstall', 2, 'PreInstall', None)]) add_data(self.db, 'InstallExecuteSequence', [('PreInstall', 'NOT Installed', 450)]) return None
def AddSPComponent(msiSrcPath, patch_level): try: FeatureName = GetFeatureParent(msiSrcPath) logger.info("FeatureName :" + str(FeatureName)) componUUID = msilib.gen_uuid() patchName = str(patchType) + str(patch_level) logger.info("patchName :" + str(patchName)) sp_product_name = '[ProductName] ' + patchName logger.info("sp_product_name :" + str(sp_product_name)) patch_reg_key = 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + sp_product_name MsiProductCode = GetProductCode(msiSrcPath) msiDB = msilib.OpenDatabase(msiSrcPath, msilib.MSIDBOPEN_TRANSACT) #add component for SP msilib.add_data( msiDB, 'Component', [(patchName + '_RegistryUninstall', componUUID, 'INSTALLDIR', 260, 'PATCH', patchName + '_UNINSTALLSTRING')]) msilib.add_data(msiDB, 'FeatureComponents', [(FeatureName, patchName + '_RegistryUninstall')]) msilib.add_data(msiDB, 'Registry', [ (patchName + '_UNINSTALLSTRING', 2, patch_reg_key, 'UninstallString', 'Msiexec.exe /uninstall ' + str(PatchUUID) + ' /package [ProductCode] /qb', patchName + '_RegistryUninstall'), (patchName + '_SHELL_ENTRY', 2, patch_reg_key, '*', '', patchName + '_RegistryUninstall'), (patchName + '_DISPLAYICON', 2, patch_reg_key, 'DisplayIcon', '[SetupFolder]SetupRes\\amar.ico', patchName + '_RegistryUninstall'), (patchName + '_DISPLAYNAME', 2, patch_reg_key, 'DisplayName', '[ProductName] ' + patchName + '-64bit ', patchName + '_RegistryUninstall'), (patchName + '_DISPLAYVERSION', 2, patch_reg_key, 'DisplayVersion', '[ProductVersion]', patchName + '_RegistryUninstall'), (patchName + '_NOMODIFY', 2, patch_reg_key, 'NoModify', '#1', patchName + '_RegistryUninstall'), (patchName + '_NOREMOVE', 2, patch_reg_key, 'NoRemove', '#0', patchName + '_RegistryUninstall'), (patchName + '_NOREPAIR', 2, patch_reg_key, 'NoRepair', '#1', patchName + '_RegistryUninstall'), (patchName + '_PARENTDISPLAYNAME', 2, patch_reg_key, 'ParentDisplayName', '[ProductName]', patchName + '_RegistryUninstall'), (patchName + '_PARENTKEYNAME', 2, patch_reg_key, 'ParentKeyName', '[ProductName]', patchName + '_RegistryUninstall'), (patchName + '_INSTALLDATE', 2, patch_reg_key, 'InstallDate', '[Date]', patchName + '_RegistryUninstall'), (patchName + '_RELEASETYPE', 2, patch_reg_key, 'ReleaseType', 'Product Update', patchName + '_RegistryUninstall'), (patchName + '_PUBLISHER', 2, patch_reg_key, 'Publisher', '[Manufacturer]', patchName + '_RegistryUninstall') ]) msiDB.Commit() except Exception as E: logger.info("Exception in AddSPComponent is:" + str(E))
def generate_msi(self, document, dstroot): """ generate msi from a document, using systescanmerge and registryscanmerge """ self.database = msilib.OpenDatabase(self.msiFile.path, msilib.MSIDBOPEN_DIRECT) self.document = document propertyRecords = self.collectPropertyRecords() table = "Property" msilib.add_data(self.database, table, propertyRecords) promptText = self.productName + " " + self.productVersion + " [1]" diskPrompt = [ ("DiskPrompt", promptText ) ] msilib.add_data(self.database, table, diskPrompt) self.cabFile = msilib.CAB("files.cab") f = msilib.Feature(self.database, "defaultFeature", "Default Feature", "Everything", 0, directory="TARGETDIR") f.set_current() home = dstroot[:3] # example home = "C:\" # this is the root directory object, a parent of all subdirs in the installation root = msilib.Directory(self.database, self.cabFile, None, home , "TARGETDIR", "SourceDir") self.__buildMSIHiarchy(dstroot, root) # create a component in "Component" table responsible for installing the registry keys root.start_component(component = self.RegsitryComponent, flags = 0) # RegsitryComponent is set to current component by default, we don't need that right now, root.component = None # Create and add registry records: self.createRegistryRecords() if len(self.registryRecords) != 0: # there are registry changes, enable and then add them to the installer actionsRecords = self.enableRegistryActions() table = "InstallExecuteSequence" msilib.add_data(self.database, table, actionsRecords) # now add self.registryRecords found by calling createRegistryRecords, to the msi. a list of tuples is the format. table = "Registry" msilib.add_data(self.database, table, self.registryRecords) self.database.Commit() self.cabFile.commit(self.database)
def add_scripts(self): distutils.command.bdist_msi.bdist_msi.add_scripts(self) msilib.add_data(self.db, "RemoveFile", [ ("cxFreezeBatch", "cx_Freeze", "cxfreeze*.bat", "Scripts", 2) ])
def add_ui(self): db = self.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 # 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"), # possible values: ALL, JUSTME ("WhichUsers", "ALL") ]) # Fonts, see "TextStyle Table" add_data( db, "TextStyle", [ ("DlgFont8", "Tahoma", 9, None, 0), ("DlgFontBold8", "Tahoma", 8, None, 1), #bold ("VerdanaBold10", "Verdana", 10, None, 1), ("VerdanaRed9", "Verdana", 9, 255, 0), ]) # 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), # In the user interface, assume all-users installation if privileged. ("SelectFeaturesDlg", "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, 'ActionText', text.ActionText) add_data(db, 'UIText', text.UIText) ##################################################################### # 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", 15, 70, 320, 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", 15, 155, 320, 20, 0x30003, "Click the Finish button to exit the Installer.") c = fatal.next("Finish", "Cancel", name="Finish") c.event("EndDialog", "Exit") 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", 15, 70, 320, 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", 15, 155, 320, 20, 0x30003, "Click the Finish button to exit the Installer.") c = user_exit.next("Finish", "Cancel", name="Finish") c.event("EndDialog", "Exit") exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title, "Finish", "Finish", "Finish") exit_dialog.title("Completing the [ProductName] Installer") exit_dialog.back("< Back", "Finish", active=0) exit_dialog.cancel("Cancel", "Back", active=0) exit_dialog.text("Description", 15, 235, 320, 20, 0x30003, "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") cancel.text( "Text", 48, 15, 194, 30, 3, "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") 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." ) 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", 15, 70, 320, 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", 15, 110, 320, 20, 0x30003, "Pondering...") c.mapping("ActionText", "Text") c = prep.text("ActionData", 15, 135, 320, 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") ##################################################################### # Feature (Python directory) selection seldlg = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal, title, "Next", "Next", "Cancel") seldlg.title("Select Python Installations") seldlg.text( "Hint", 15, 30, 300, 20, 3, "Select the Python locations where %s should be installed." % self.distribution.get_fullname()) seldlg.back("< Back", None, active=0) c = seldlg.next("Next >", "Cancel") order = 1 c.event("[TARGETDIR]", "[SourceDir]", ordering=order) for version in self.versions + [self.other_version]: order += 1 c.event("[TARGETDIR]", "[TARGETDIR%s]" % version, "FEATURE_SELECTED AND &Python%s=3" % version, ordering=order) c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=order + 1) c.event("EndDialog", "Return", ordering=order + 2) c = seldlg.cancel("Cancel", "Features") c.event("SpawnDialog", "CancelDlg") c = seldlg.control("Features", "SelectionTree", 15, 60, 300, 120, 3, "FEATURE", None, "PathEdit", None) c.event("[FEATURE_SELECTED]", "1") ver = self.other_version install_other_cond = "FEATURE_SELECTED AND &Python%s=3" % ver dont_install_other_cond = "FEATURE_SELECTED AND &Python%s<>3" % ver c = seldlg.text("Other", 15, 200, 300, 15, 3, "Provide an alternate Python location") c.condition("Enable", install_other_cond) c.condition("Show", install_other_cond) c.condition("Disable", dont_install_other_cond) c.condition("Hide", dont_install_other_cond) c = seldlg.control("PathEdit", "PathEdit", 15, 215, 300, 16, 1, "TARGETDIR" + ver, None, "Next", None) c.condition("Enable", install_other_cond) c.condition("Show", install_other_cond) c.condition("Disable", dont_install_other_cond) c.condition("Hide", dont_install_other_cond) ##################################################################### # 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, r"{\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 g = whichusers.radiogroup("AdminInstall", 15, 60, 260, 50, 3, "WhichUsers", "", "Next") g.add("ALL", 0, 5, 150, 20, "Install for all users") g.add("JUSTME", 0, 25, 150, 20, "Install just for me") whichusers.back("Back", None, active=0) c = whichusers.next("Next >", "Cancel") c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1) c.event("EndDialog", "Return", ordering=2) c = whichusers.cancel("Cancel", "AdminInstall") c.event("SpawnDialog", "CancelDlg") ##################################################################### # 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, r"{\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", 15, 63, 330, 42, 3, "Select whether you want to repair or remove [ProductName].") g = maint.radiogroup("RepairRadioGroup", 15, 108, 330, 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]") 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) c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7) 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) # Close dialog when maintenance action scheduled c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20) #c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21) maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")
def add_find_python(self): """Adds code to the installer to compute the location of Python. Properties PYTHON.MACHINE.X.Y and PYTHON.USER.X.Y will be set from the registry for each version of Python. Properties TARGETDIRX.Y will be set from PYTHON.USER.X.Y if defined, else from PYTHON.MACHINE.X.Y. Properties PYTHONX.Y will be set to TARGETDIRX.Y\\python.exe""" start = 402 for ver in self.versions: install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % ver machine_reg = "python.machine." + ver user_reg = "python.user." + ver machine_prop = "PYTHON.MACHINE." + ver user_prop = "PYTHON.USER." + ver machine_action = "PythonFromMachine" + ver user_action = "PythonFromUser" + ver exe_action = "PythonExe" + ver target_dir_prop = "TARGETDIR" + ver exe_prop = "PYTHON" + ver if msilib.Win64: # type: msidbLocatorTypeRawValue + msidbLocatorType64bit Type = 2 + 16 else: Type = 2 add_data(self.db, "RegLocator", [(machine_reg, 2, install_path, None, Type), (user_reg, 1, install_path, None, Type)]) add_data(self.db, "AppSearch", [(machine_prop, machine_reg), (user_prop, user_reg)]) add_data(self.db, "CustomAction", [ (machine_action, 51 + 256, target_dir_prop, "[" + machine_prop + "]"), (user_action, 51 + 256, target_dir_prop, "[" + user_prop + "]"), (exe_action, 51 + 256, exe_prop, "[" + target_dir_prop + "]\\python.exe"), ]) add_data(self.db, "InstallExecuteSequence", [ (machine_action, machine_prop, start), (user_action, user_prop, start + 1), (exe_action, None, start + 2), ]) add_data(self.db, "InstallUISequence", [ (machine_action, machine_prop, start), (user_action, user_prop, start + 1), (exe_action, None, start + 2), ]) add_data(self.db, "Condition", [("Python" + ver, 0, "NOT TARGETDIR" + ver)]) start += 4 assert start < 500
def run(self): if not self.skip_build: self.run_command('build') install = self.reinitialize_command('install', reinit_subcommands=1) install.prefix = self.bdist_dir install.skip_build = self.skip_build install.warn_dir = 0 install_lib = self.reinitialize_command('install_lib') # we do not want to include pyc or pyo files install_lib.compile = 0 install_lib.optimize = 0 if self.distribution.has_ext_modules(): # If we are building an installer for a Python version other # than the one we are currently running, then we need to ensure # our build_lib reflects the other Python version rather than ours. # Note that for target_version!=sys.version, we must have skipped the # build step, so there is no issue with enforcing the build of this # version. target_version = self.target_version if not target_version: assert self.skip_build, "Should have already checked this" target_version = '%d.%d' % sys.version_info[:2] plat_specifier = ".%s-%s" % (self.plat_name, target_version) build = self.get_finalized_command('build') build.build_lib = os.path.join(build.build_base, 'lib' + plat_specifier) log.info("installing to %s", self.bdist_dir) install.ensure_finalized() # avoid warning of 'install_lib' about installing # into a directory not in sys.path sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) install.run() del sys.path[0] self.mkpath(self.dist_dir) fullname = self.distribution.get_fullname() installer_name = self.get_installer_filename(fullname) installer_name = os.path.abspath(installer_name) if os.path.exists(installer_name): os.unlink(installer_name) metadata = self.distribution.metadata author = metadata.author if not author: author = metadata.maintainer if not author: author = "UNKNOWN" version = metadata.get_version() # ProductVersion must be strictly numeric # XXX need to deal with prerelease versions sversion = "%d.%d.%d" % StrictVersion(version).version # Prefix ProductName with Python x.y, so that # it sorts together with the other Python packages # in Add-Remove-Programs (APR) fullname = self.distribution.get_fullname() if self.target_version: product_name = "Python %s %s" % (self.target_version, fullname) else: product_name = "Python %s" % (fullname) self.db = msilib.init_database(installer_name, schema, product_name, msilib.gen_uuid(), sversion, author) msilib.add_tables(self.db, sequence) props = [('DistVersion', version)] email = metadata.author_email or metadata.maintainer_email if email: props.append(("ARPCONTACT", email)) if metadata.url: props.append(("ARPURLINFOABOUT", metadata.url)) if props: add_data(self.db, 'Property', props) self.add_find_python() self.add_files() self.add_scripts() self.add_ui() self.db.Commit() if hasattr(self.distribution, 'dist_files'): tup = 'bdist_msi', self.target_version or 'any', fullname self.distribution.dist_files.append(tup) if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run)
def run(self): if not self.skip_build: self.run_command('build') install = self.reinitialize_command('install', reinit_subcommands=1) install.prefix = self.bdist_dir install.skip_build = self.skip_build install.warn_dir = 0 install_lib = self.reinitialize_command('install_lib') install_lib.compile = 0 install_lib.optimize = 0 if self.distribution.has_ext_modules(): target_version = self.target_version if not target_version: target_version = sys.version[0:3] plat_specifier = '.%s-%s' % (self.plat_name, target_version) build = self.get_finalized_command('build') build.build_lib = os.path.join(build.build_base, 'lib' + plat_specifier) log.info('installing to %s', self.bdist_dir) install.ensure_finalized() sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) install.run() del sys.path[0] self.mkpath(self.dist_dir) fullname = self.distribution.get_fullname() installer_name = self.get_installer_filename(fullname) installer_name = os.path.abspath(installer_name) if os.path.exists(installer_name): os.unlink(installer_name) metadata = self.distribution.metadata author = metadata.author if not author: author = metadata.maintainer if not author: author = 'UNKNOWN' version = metadata.get_version() sversion = '%d.%d.%d' % StrictVersion(version).version fullname = self.distribution.get_fullname() if self.target_version: product_name = 'Python %s %s' % (self.target_version, fullname) else: product_name = 'Python %s' % fullname self.db = msilib.init_database(installer_name, schema, product_name, msilib.gen_uuid(), sversion, author) msilib.add_tables(self.db, sequence) props = [('DistVersion', version)] email = metadata.author_email or metadata.maintainer_email if email: props.append(('ARPCONTACT', email)) if metadata.url: props.append(('ARPURLINFOABOUT', metadata.url)) if props: add_data(self.db, 'Property', props) self.add_find_python() self.add_files() self.add_scripts() self.add_ui() self.db.Commit() if hasattr(self.distribution, 'dist_files'): tup = ('bdist_msi', self.target_version or 'any', fullname) self.distribution.dist_files.append(tup) if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run)
def add_ui(self): db = self.db x = y = 50 w = 370 h = 300 title = '[ProductName] Setup' modal = 3 modeless = 1 add_data(db, 'Property', [('DefaultUIFont', 'DlgFont8'), ('ErrorDialog', 'ErrorDlg'), ('Progress1', 'Install'), ('Progress2', 'installs'), ('MaintenanceForm_Action', 'Repair'), ('WhichUsers', 'ALL')]) add_data(db, 'TextStyle', [('DlgFont8', 'Tahoma', 9, None, 0), ('DlgFontBold8', 'Tahoma', 8, None, 1), ('VerdanaBold10', 'Verdana', 10, None, 1), ('VerdanaRed9', 'Verdana', 9, 255, 0)]) add_data( db, 'InstallUISequence', [('PrepareDlg', 'Not Privileged or Windows9x or Installed', 140), ('WhichUsersDlg', 'Privileged and not Windows9x and not Installed', 141), ('SelectFeaturesDlg', 'Not Installed', 1230), ('MaintenanceTypeDlg', 'Installed AND NOT RESUME AND NOT Preselected', 1250), ('ProgressDlg', None, 1280)]) add_data(db, 'ActionText', text.ActionText) add_data(db, 'UIText', text.UIText) 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', 15, 70, 320, 80, 196611, '[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', 15, 155, 320, 20, 196611, 'Click the Finish button to exit the Installer.') c = fatal.next('Finish', 'Cancel', name='Finish') c.event('EndDialog', 'Exit') 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', 15, 70, 320, 80, 196611, '[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', 15, 155, 320, 20, 196611, 'Click the Finish button to exit the Installer.') c = user_exit.next('Finish', 'Cancel', name='Finish') c.event('EndDialog', 'Exit') exit_dialog = PyDialog(db, 'ExitDialog', x, y, w, h, modal, title, 'Finish', 'Finish', 'Finish') exit_dialog.title('Completing the [ProductName] Installer') exit_dialog.back('< Back', 'Finish', active=0) exit_dialog.cancel('Cancel', 'Back', active=0) exit_dialog.text('Description', 15, 235, 320, 20, 196611, 'Click the Finish button to exit the Installer.') c = exit_dialog.next('Finish', 'Cancel', name='Finish') c.event('EndDialog', 'Return') inuse = PyDialog(db, 'FilesInUse', x, y, w, h, 19, title, 'Retry', 'Retry', 'Retry', bitmap=False) inuse.text('Title', 15, 6, 200, 15, 196611, '{\\DlgFontBold8}Files in Use') inuse.text('Description', 20, 23, 280, 20, 196611, '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') error = Dialog(db, 'ErrorDlg', 50, 10, 330, 101, 65543, title, 'ErrorText', None, None) error.text('ErrorText', 50, 9, 280, 48, 3, '') 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') cancel = Dialog(db, 'CancelDlg', 50, 10, 260, 85, 3, title, 'No', 'No', 'No') cancel.text( 'Text', 48, 15, 194, 30, 3, 'Are you sure you want to cancel [ProductName] installation?') c = cancel.pushbutton('Yes', 72, 57, 56, 17, 3, 'Yes', 'No') c.event('EndDialog', 'Exit') c = cancel.pushbutton('No', 132, 57, 56, 17, 3, 'No', 'Yes') c.event('EndDialog', 'Return') 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.' ) c = costing.pushbutton('Return', 102, 57, 56, 17, 3, 'Return', None) c.event('EndDialog', 'Exit') prep = PyDialog(db, 'PrepareDlg', x, y, w, h, modeless, title, 'Cancel', 'Cancel', 'Cancel') prep.text( 'Description', 15, 70, 320, 40, 196611, 'Please wait while the Installer prepares to guide you through the installation.' ) prep.title('Welcome to the [ProductName] Installer') c = prep.text('ActionText', 15, 110, 320, 20, 196611, 'Pondering...') c.mapping('ActionText', 'Text') c = prep.text('ActionData', 15, 135, 320, 30, 196611, 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') seldlg = PyDialog(db, 'SelectFeaturesDlg', x, y, w, h, modal, title, 'Next', 'Next', 'Cancel') seldlg.title('Select Python Installations') seldlg.text( 'Hint', 15, 30, 300, 20, 3, 'Select the Python locations where %s should be installed.' % self.distribution.get_fullname()) seldlg.back('< Back', None, active=0) c = seldlg.next('Next >', 'Cancel') order = 1 c.event('[TARGETDIR]', '[SourceDir]', ordering=order) for version in self.versions + [self.other_version]: order += 1 c.event('[TARGETDIR]', '[TARGETDIR%s]' % version, 'FEATURE_SELECTED AND &Python%s=3' % version, ordering=order) c.event('SpawnWaitDialog', 'WaitForCostingDlg', ordering=order + 1) c.event('EndDialog', 'Return', ordering=order + 2) c = seldlg.cancel('Cancel', 'Features') c.event('SpawnDialog', 'CancelDlg') c = seldlg.control('Features', 'SelectionTree', 15, 60, 300, 120, 3, 'FEATURE', None, 'PathEdit', None) c.event('[FEATURE_SELECTED]', '1') ver = self.other_version install_other_cond = 'FEATURE_SELECTED AND &Python%s=3' % ver dont_install_other_cond = 'FEATURE_SELECTED AND &Python%s<>3' % ver c = seldlg.text('Other', 15, 200, 300, 15, 3, 'Provide an alternate Python location') c.condition('Enable', install_other_cond) c.condition('Show', install_other_cond) c.condition('Disable', dont_install_other_cond) c.condition('Hide', dont_install_other_cond) c = seldlg.control('PathEdit', 'PathEdit', 15, 215, 300, 16, 1, 'TARGETDIR' + ver, None, 'Next', None) c.condition('Enable', install_other_cond) c.condition('Show', install_other_cond) c.condition('Disable', dont_install_other_cond) c.condition('Hide', dont_install_other_cond) cost = PyDialog(db, 'DiskCostDlg', x, y, w, h, modal, title, 'OK', 'OK', 'OK', bitmap=False) cost.text('Title', 15, 6, 200, 15, 196611, '{\\DlgFontBold8}Disk Space Requirements') cost.text( 'Description', 20, 20, 280, 20, 196611, '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 = 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.' ) g = whichusers.radiogroup('AdminInstall', 15, 60, 260, 50, 3, 'WhichUsers', '', 'Next') g.add('ALL', 0, 5, 150, 20, 'Install for all users') g.add('JUSTME', 0, 25, 150, 20, 'Install just for me') whichusers.back('Back', None, active=0) c = whichusers.next('Next >', 'Cancel') c.event('[ALLUSERS]', '1', 'WhichUsers="ALL"', 1) c.event('EndDialog', 'Return', ordering=2) c = whichusers.cancel('Cancel', 'AdminInstall') c.event('SpawnDialog', 'CancelDlg') progress = PyDialog(db, 'ProgressDlg', x, y, w, h, modeless, title, 'Cancel', 'Cancel', 'Cancel', bitmap=False) progress.text('Title', 20, 15, 200, 15, 196611, '{\\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.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') maint = PyDialog(db, 'MaintenanceTypeDlg', x, y, w, h, modal, title, 'Next', 'Next', 'Cancel') maint.title('Welcome to the [ProductName] Setup Wizard') maint.text( 'BodyText', 15, 63, 330, 42, 3, 'Select whether you want to repair or remove [ProductName].') g = maint.radiogroup('RepairRadioGroup', 15, 108, 330, 60, 3, 'MaintenanceForm_Action', '', 'Next') g.add('Repair', 0, 18, 200, 17, '&Repair [ProductName]') g.add('Remove', 0, 36, 200, 17, 'Re&move [ProductName]') maint.back('< Back', None, active=False) c = maint.next('Finish', 'Cancel') c.event('[REINSTALL]', 'ALL', 'MaintenanceForm_Action="Repair"', 5) c.event('[Progress1]', 'Repairing', 'MaintenanceForm_Action="Repair"', 6) c.event('[Progress2]', 'repairs', 'MaintenanceForm_Action="Repair"', 7) c.event('Reinstall', 'ALL', 'MaintenanceForm_Action="Repair"', 8) 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) c.event('EndDialog', 'Return', 'MaintenanceForm_Action<>"Change"', 20) maint.cancel('Cancel', 'RepairRadioGroup').event('SpawnDialog', 'CancelDlg') return
def add_find_python(self): start = 402 for ver in self.versions: install_path = 'SOFTWARE\\Python\\PythonCore\\%s\\InstallPath' % ver machine_reg = 'python.machine.' + ver user_reg = 'python.user.' + ver machine_prop = 'PYTHON.MACHINE.' + ver user_prop = 'PYTHON.USER.' + ver machine_action = 'PythonFromMachine' + ver user_action = 'PythonFromUser' + ver exe_action = 'PythonExe' + ver target_dir_prop = 'TARGETDIR' + ver exe_prop = 'PYTHON' + ver if msilib.Win64: Type = 18 else: Type = 2 add_data(self.db, 'RegLocator', [(machine_reg, 2, install_path, None, Type), (user_reg, 1, install_path, None, Type)]) add_data(self.db, 'AppSearch', [(machine_prop, machine_reg), (user_prop, user_reg)]) add_data( self.db, 'CustomAction', [(machine_action, 307, target_dir_prop, '[' + machine_prop + ']'), (user_action, 307, target_dir_prop, '[' + user_prop + ']'), (exe_action, 307, exe_prop, '[' + target_dir_prop + ']\\python.exe')]) add_data(self.db, 'InstallExecuteSequence', [(machine_action, machine_prop, start), (user_action, user_prop, start + 1), (exe_action, None, start + 2)]) add_data(self.db, 'InstallUISequence', [(machine_action, machine_prop, start), (user_action, user_prop, start + 1), (exe_action, None, start + 2)]) add_data(self.db, 'Condition', [('Python' + ver, 0, 'NOT TARGETDIR' + ver)]) start += 4 return