mirror of https://github.com/python/cpython
Import bdist_msi
This commit is contained in:
parent
99b93c2824
commit
fbab90e95b
|
@ -0,0 +1,639 @@
|
|||
# -*- coding: iso-8859-1 -*-
|
||||
# Copyright (C) 2005 Martin v. Löwis
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
# The bdist_wininst command proper
|
||||
# based on bdist_wininst
|
||||
"""
|
||||
Implements the bdist_msi command.
|
||||
"""
|
||||
|
||||
import sys, os, string
|
||||
from distutils.core import Command
|
||||
from distutils.util import get_platform
|
||||
from distutils.dir_util import remove_tree
|
||||
from distutils.sysconfig import get_python_version
|
||||
from distutils.version import StrictVersion
|
||||
from distutils.errors import DistutilsOptionError
|
||||
from distutils import log
|
||||
import msilib
|
||||
from msilib import schema, sequence, uisample
|
||||
from msilib import Directory, Feature, Dialog, add_data
|
||||
|
||||
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", 15, 10, 320, 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)
|
||||
|
||||
class bdist_msi (Command):
|
||||
|
||||
description = "create a Microsoft Installer (.msi) binary distribution"
|
||||
|
||||
user_options = [('bdist-dir=', None,
|
||||
"temporary directory for creating the distribution"),
|
||||
('keep-temp', 'k',
|
||||
"keep the pseudo-installation tree around after " +
|
||||
"creating the distribution archive"),
|
||||
('target-version=', None,
|
||||
"require a specific python version" +
|
||||
" on the target system"),
|
||||
('no-target-compile', 'c',
|
||||
"do not compile .py to .pyc on the target system"),
|
||||
('no-target-optimize', 'o',
|
||||
"do not compile .py to .pyo (optimized)"
|
||||
"on the target system"),
|
||||
('dist-dir=', 'd',
|
||||
"directory to put final built distributions in"),
|
||||
('skip-build', None,
|
||||
"skip rebuilding everything (for testing/debugging)"),
|
||||
('install-script=', None,
|
||||
"basename of installation script to be run after"
|
||||
"installation or before deinstallation"),
|
||||
('pre-install-script=', None,
|
||||
"Fully qualified filename of a script to be run before "
|
||||
"any files are installed. This script need not be in the "
|
||||
"distribution"),
|
||||
]
|
||||
|
||||
boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize',
|
||||
'skip-build']
|
||||
|
||||
def initialize_options (self):
|
||||
self.bdist_dir = None
|
||||
self.keep_temp = 0
|
||||
self.no_target_compile = 0
|
||||
self.no_target_optimize = 0
|
||||
self.target_version = None
|
||||
self.dist_dir = None
|
||||
self.skip_build = 0
|
||||
self.install_script = None
|
||||
self.pre_install_script = None
|
||||
|
||||
def finalize_options (self):
|
||||
if self.bdist_dir is None:
|
||||
bdist_base = self.get_finalized_command('bdist').bdist_base
|
||||
self.bdist_dir = os.path.join(bdist_base, 'msi')
|
||||
short_version = get_python_version()
|
||||
if self.target_version:
|
||||
if not self.skip_build and self.distribution.has_ext_modules()\
|
||||
and self.target_version != short_version:
|
||||
raise DistutilsOptionError, \
|
||||
"target version can only be %s, or the '--skip_build'" \
|
||||
" option must be specified" % (short_version,)
|
||||
else:
|
||||
self.target_version = short_version
|
||||
|
||||
self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
|
||||
|
||||
if self.pre_install_script:
|
||||
raise DistutilsOptionError, "the pre-install-script feature is not yet implemented"
|
||||
|
||||
if self.install_script:
|
||||
for script in self.distribution.scripts:
|
||||
if self.install_script == os.path.basename(script):
|
||||
break
|
||||
else:
|
||||
raise DistutilsOptionError, \
|
||||
"install_script '%s' not found in scripts" % \
|
||||
self.install_script
|
||||
self.install_script_key = None
|
||||
# finalize_options()
|
||||
|
||||
|
||||
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 = sys.version[0:3]
|
||||
plat_specifier = ".%s-%s" % (get_platform(), 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)
|
||||
product_name = "Python %s %s" % (self.target_version,
|
||||
self.distribution.get_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'):
|
||||
self.distribution.dist_files.append(('bdist_msi', self.target_version, fullname))
|
||||
|
||||
if not self.keep_temp:
|
||||
remove_tree(self.bdist_dir, dry_run=self.dry_run)
|
||||
|
||||
def add_files(self):
|
||||
db = self.db
|
||||
cab = msilib.CAB("distfiles")
|
||||
f = Feature(db, "default", "Default Feature", "Everything", 1, directory="TARGETDIR")
|
||||
f.set_current()
|
||||
rootdir = os.path.abspath(self.bdist_dir)
|
||||
root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir")
|
||||
db.Commit()
|
||||
todo = [root]
|
||||
while todo:
|
||||
dir = todo.pop()
|
||||
for file in os.listdir(dir.absolute):
|
||||
afile = os.path.join(dir.absolute, file)
|
||||
if os.path.isdir(afile):
|
||||
newdir = Directory(db, cab, dir, file, file, "%s|%s" % (dir.make_short(file), file))
|
||||
todo.append(newdir)
|
||||
else:
|
||||
key = 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
|
||||
|
||||
cab.commit(db)
|
||||
|
||||
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:
|
||||
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_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.
|
||||
("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, 'ActionText', uisample.ActionText)
|
||||
add_data(db, 'UIText', uisample.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")
|
||||
|
||||
#####################################################################
|
||||
# Target directory selection
|
||||
seldlg = PyDialog(db, "SelectDirectoryDlg", x, y, w, h, modal, title,
|
||||
"Next", "Next", "Cancel")
|
||||
seldlg.title("Select Destination Directory")
|
||||
|
||||
version = sys.version[:3]+" "
|
||||
seldlg.text("Hint", 15, 30, 300, 40, 3,
|
||||
"The destination directory should contain a Python %sinstallation" % version)
|
||||
|
||||
seldlg.back("< Back", None, active=0)
|
||||
c = seldlg.next("Next >", "Cancel")
|
||||
c.event("SetTargetPath", "TARGETDIR", order=1)
|
||||
c.event("SpawnWaitDialog", "WaitForCostingDlg", order=2)
|
||||
c.event("EndDialog", "Return", order=3)
|
||||
|
||||
c = seldlg.cancel("Cancel", "DirectoryCombo")
|
||||
c.event("SpawnDialog", "CancelDlg")
|
||||
|
||||
seldlg.control("DirectoryCombo", "DirectoryCombo", 15, 70, 272, 80, 393219,
|
||||
"TARGETDIR", None, "DirectoryList", None)
|
||||
seldlg.control("DirectoryList", "DirectoryList", 15, 90, 308, 136, 3, "TARGETDIR",
|
||||
None, "PathEdit", None)
|
||||
seldlg.control("PathEdit", "PathEdit", 15, 230, 306, 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")
|
||||
|
||||
#####################################################################
|
||||
# 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
|
||||
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", order = 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,
|
||||
"{\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 get_installer_filename(self, fullname):
|
||||
# Factored out to allow overriding in subclasses
|
||||
installer_name = os.path.join(self.dist_dir,
|
||||
"%s.win32-py%s.msi" %
|
||||
(fullname, self.target_version))
|
||||
return installer_name
|
|
@ -0,0 +1,462 @@
|
|||
# -*- coding: iso-8859-1 -*-
|
||||
# Copyright (C) 2005 Martin v. Löwis
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
from _msi import *
|
||||
import sets, os, string, re
|
||||
|
||||
Win64=0
|
||||
|
||||
# Partially taken from Wine
|
||||
datasizemask= 0x00ff
|
||||
type_valid= 0x0100
|
||||
type_localizable= 0x0200
|
||||
|
||||
typemask= 0x0c00
|
||||
type_long= 0x0000
|
||||
type_short= 0x0400
|
||||
type_string= 0x0c00
|
||||
type_binary= 0x0800
|
||||
|
||||
type_nullable= 0x1000
|
||||
type_key= 0x2000
|
||||
# XXX temporary, localizable?
|
||||
knownbits = datasizemask | type_valid | type_localizable | \
|
||||
typemask | type_nullable | type_key
|
||||
|
||||
class Table:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.fields = []
|
||||
|
||||
def add_field(self, index, name, type):
|
||||
self.fields.append((index,name,type))
|
||||
|
||||
def sql(self):
|
||||
fields = []
|
||||
keys = []
|
||||
self.fields.sort()
|
||||
fields = [None]*len(self.fields)
|
||||
for index, name, type in self.fields:
|
||||
index -= 1
|
||||
unk = type & ~knownbits
|
||||
if unk:
|
||||
print "%s.%s unknown bits %x" % (self.name, name, unk)
|
||||
size = type & datasizemask
|
||||
dtype = type & typemask
|
||||
if dtype == type_string:
|
||||
if size:
|
||||
tname="CHAR(%d)" % size
|
||||
else:
|
||||
tname="CHAR"
|
||||
elif dtype == type_short:
|
||||
assert size==2
|
||||
tname = "SHORT"
|
||||
elif dtype == type_long:
|
||||
assert size==4
|
||||
tname="LONG"
|
||||
elif dtype == type_binary:
|
||||
assert size==0
|
||||
tname="OBJECT"
|
||||
else:
|
||||
tname="unknown"
|
||||
print "%s.%sunknown integer type %d" % (self.name, name, size)
|
||||
if type & type_nullable:
|
||||
flags = ""
|
||||
else:
|
||||
flags = " NOT NULL"
|
||||
if type & type_localizable:
|
||||
flags += " LOCALIZABLE"
|
||||
fields[index] = "`%s` %s%s" % (name, tname, flags)
|
||||
if type & type_key:
|
||||
keys.append("`%s`" % name)
|
||||
fields = ", ".join(fields)
|
||||
keys = ", ".join(keys)
|
||||
return "CREATE TABLE %s (%s PRIMARY KEY %s)" % (self.name, fields, keys)
|
||||
|
||||
def create(self, db):
|
||||
v = db.OpenView(self.sql())
|
||||
v.Execute(None)
|
||||
v.Close()
|
||||
|
||||
class _Unspecified:pass
|
||||
def change_sequence(seq, action, seqno=_Unspecified, cond = _Unspecified):
|
||||
"Change the sequence number of an action in a sequence list"
|
||||
for i in range(len(seq)):
|
||||
if seq[i][0] == action:
|
||||
if cond is _Unspecified:
|
||||
cond = seq[i][1]
|
||||
if seqno is _Unspecified:
|
||||
seqno = seq[i][2]
|
||||
seq[i] = (action, cond, seqno)
|
||||
return
|
||||
raise ValueError, "Action not found in sequence"
|
||||
|
||||
def add_data(db, table, values):
|
||||
v = db.OpenView("SELECT * FROM `%s`" % table)
|
||||
count = v.GetColumnInfo(MSICOLINFO_NAMES).GetFieldCount()
|
||||
r = CreateRecord(count)
|
||||
for value in values:
|
||||
assert len(value) == count, value
|
||||
for i in range(count):
|
||||
field = value[i]
|
||||
if isinstance(field, (int, long)):
|
||||
r.SetInteger(i+1,field)
|
||||
elif isinstance(field, basestring):
|
||||
r.SetString(i+1,field)
|
||||
elif field is None:
|
||||
pass
|
||||
elif isinstance(field, Binary):
|
||||
r.SetStream(i+1, field.name)
|
||||
else:
|
||||
raise TypeError, "Unsupported type %s" % field.__class__.__name__
|
||||
try:
|
||||
v.Modify(MSIMODIFY_INSERT, r)
|
||||
except Exception, e:
|
||||
raise MSIError("Could not insert "+repr(values)+" into "+table)
|
||||
|
||||
r.ClearData()
|
||||
v.Close()
|
||||
|
||||
|
||||
def add_stream(db, name, path):
|
||||
v = db.OpenView("INSERT INTO _Streams (Name, Data) VALUES ('%s', ?)" % name)
|
||||
r = CreateRecord(1)
|
||||
r.SetStream(1, path)
|
||||
v.Execute(r)
|
||||
v.Close()
|
||||
|
||||
def init_database(name, schema,
|
||||
ProductName, ProductCode, ProductVersion,
|
||||
Manufacturer):
|
||||
try:
|
||||
os.unlink(name)
|
||||
except OSError:
|
||||
pass
|
||||
ProductCode = ProductCode.upper()
|
||||
# Create the database
|
||||
db = OpenDatabase(name, MSIDBOPEN_CREATE)
|
||||
# Create the tables
|
||||
for t in schema.tables:
|
||||
t.create(db)
|
||||
# Fill the validation table
|
||||
add_data(db, "_Validation", schema._Validation_records)
|
||||
# Initialize the summary information, allowing atmost 20 properties
|
||||
si = db.GetSummaryInformation(20)
|
||||
si.SetProperty(PID_TITLE, "Installation Database")
|
||||
si.SetProperty(PID_SUBJECT, ProductName)
|
||||
si.SetProperty(PID_AUTHOR, Manufacturer)
|
||||
if Win64:
|
||||
si.SetProperty(PID_TEMPLATE, "Intel64;1033")
|
||||
else:
|
||||
si.SetProperty(PID_TEMPLATE, "Intel;1033")
|
||||
si.SetProperty(PID_REVNUMBER, gen_uuid())
|
||||
si.SetProperty(PID_WORDCOUNT, 2) # long file names, compressed, original media
|
||||
si.SetProperty(PID_PAGECOUNT, 200)
|
||||
si.SetProperty(PID_APPNAME, "Python MSI Library")
|
||||
# XXX more properties
|
||||
si.Persist()
|
||||
add_data(db, "Property", [
|
||||
("ProductName", ProductName),
|
||||
("ProductCode", ProductCode),
|
||||
("ProductVersion", ProductVersion),
|
||||
("Manufacturer", Manufacturer),
|
||||
("ProductLanguage", "1033")])
|
||||
db.Commit()
|
||||
return db
|
||||
|
||||
def add_tables(db, module):
|
||||
for table in module.tables:
|
||||
add_data(db, table, getattr(module, table))
|
||||
|
||||
def make_id(str):
|
||||
#str = str.replace(".", "_") # colons are allowed
|
||||
str = str.replace(" ", "_")
|
||||
str = str.replace("-", "_")
|
||||
if str[0] in string.digits:
|
||||
str = "_"+str
|
||||
assert re.match("^[A-Za-z_][A-Za-z0-9_.]*$", str), "FILE"+str
|
||||
return str
|
||||
|
||||
def gen_uuid():
|
||||
return "{"+UuidCreate().upper()+"}"
|
||||
|
||||
class CAB:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.files = []
|
||||
self.filenames = sets.Set()
|
||||
self.index = 0
|
||||
|
||||
def gen_id(self, dir, file):
|
||||
logical = _logical = make_id(file)
|
||||
pos = 1
|
||||
while logical in self.filenames:
|
||||
logical = "%s.%d" % (_logical, pos)
|
||||
pos += 1
|
||||
self.filenames.add(logical)
|
||||
return logical
|
||||
|
||||
def append(self, full, file, logical = None):
|
||||
if os.path.isdir(full):
|
||||
return
|
||||
if not logical:
|
||||
logical = self.gen_id(dir, file)
|
||||
self.index += 1
|
||||
self.files.append((full, logical))
|
||||
return self.index, logical
|
||||
|
||||
def commit(self, db):
|
||||
from tempfile import mktemp
|
||||
filename = mktemp()
|
||||
FCICreate(filename, self.files)
|
||||
add_data(db, "Media",
|
||||
[(1, self.index, None, "#"+self.name, None, None)])
|
||||
add_stream(db, self.name, filename)
|
||||
os.unlink(filename)
|
||||
db.Commit()
|
||||
|
||||
_directories = sets.Set()
|
||||
class Directory:
|
||||
def __init__(self, db, cab, basedir, physical, _logical, default, componentflags=None):
|
||||
"""Create a new directory in the Directory table. There is a current component
|
||||
at each point in time for the directory, which is either explicitly created
|
||||
through start_component, or implicitly when files are added for the first
|
||||
time. Files are added into the current component, and into the cab file.
|
||||
To create a directory, a base directory object needs to be specified (can be
|
||||
None), the path to the physical directory, and a logical directory name.
|
||||
Default specifies the DefaultDir slot in the directory table. componentflags
|
||||
specifies the default flags that new components get."""
|
||||
index = 1
|
||||
_logical = make_id(_logical)
|
||||
logical = _logical
|
||||
while logical in _directories:
|
||||
logical = "%s%d" % (_logical, index)
|
||||
index += 1
|
||||
_directories.add(logical)
|
||||
self.db = db
|
||||
self.cab = cab
|
||||
self.basedir = basedir
|
||||
self.physical = physical
|
||||
self.logical = logical
|
||||
self.component = None
|
||||
self.short_names = sets.Set()
|
||||
self.ids = sets.Set()
|
||||
self.keyfiles = {}
|
||||
self.componentflags = componentflags
|
||||
if basedir:
|
||||
self.absolute = os.path.join(basedir.absolute, physical)
|
||||
blogical = basedir.logical
|
||||
else:
|
||||
self.absolute = physical
|
||||
blogical = None
|
||||
add_data(db, "Directory", [(logical, blogical, default)])
|
||||
|
||||
def start_component(self, component = None, feature = None, flags = None, keyfile = None, uuid=None):
|
||||
"""Add an entry to the Component table, and make this component the current for this
|
||||
directory. If no component name is given, the directory name is used. If no feature
|
||||
is given, the current feature is used. If no flags are given, the directory's default
|
||||
flags are used. If no keyfile is given, the KeyPath is left null in the Component
|
||||
table."""
|
||||
if flags is None:
|
||||
flags = self.componentflags
|
||||
if uuid is None:
|
||||
uuid = gen_uuid()
|
||||
else:
|
||||
uuid = uuid.upper()
|
||||
if component is None:
|
||||
component = self.logical
|
||||
self.component = component
|
||||
if Win64:
|
||||
flags |= 256
|
||||
if keyfile:
|
||||
keyid = self.cab.gen_id(self.absolute, keyfile)
|
||||
self.keyfiles[keyfile] = keyid
|
||||
else:
|
||||
keyid = None
|
||||
add_data(self.db, "Component",
|
||||
[(component, uuid, self.logical, flags, None, keyid)])
|
||||
if feature is None:
|
||||
feature = current_feature
|
||||
add_data(self.db, "FeatureComponents",
|
||||
[(feature.id, component)])
|
||||
|
||||
def make_short(self, file):
|
||||
parts = file.split(".")
|
||||
if len(parts)>1:
|
||||
suffix = parts[-1].upper()
|
||||
else:
|
||||
suffix = None
|
||||
prefix = parts[0].upper()
|
||||
if len(prefix) <= 8 and (not suffix or len(suffix)<=3):
|
||||
if suffix:
|
||||
file = prefix+"."+suffix
|
||||
else:
|
||||
file = prefix
|
||||
assert file not in self.short_names
|
||||
else:
|
||||
prefix = prefix[:6]
|
||||
if suffix:
|
||||
suffix = suffix[:3]
|
||||
pos = 1
|
||||
while 1:
|
||||
if suffix:
|
||||
file = "%s~%d.%s" % (prefix, pos, suffix)
|
||||
else:
|
||||
file = "%s~%d" % (prefix, pos)
|
||||
if file not in self.short_names: break
|
||||
pos += 1
|
||||
assert pos < 10000
|
||||
if pos in (10, 100, 1000):
|
||||
prefix = prefix[:-1]
|
||||
self.short_names.add(file)
|
||||
assert not re.search(r'[\?|><:/*"+,;=\[\]]', file) # restrictions on short names
|
||||
return file
|
||||
|
||||
def add_file(self, file, src=None, version=None, language=None):
|
||||
"""Add a file to the current component of the directory, starting a new one
|
||||
one if there is no current component. By default, the file name in the source
|
||||
and the file table will be identical. If the src file is specified, it is
|
||||
interpreted relative to the current directory. Optionally, a version and a
|
||||
language can be specified for the entry in the File table."""
|
||||
if not self.component:
|
||||
self.start_component(self.logical, current_feature, 0)
|
||||
if not src:
|
||||
# Allow relative paths for file if src is not specified
|
||||
src = file
|
||||
file = os.path.basename(file)
|
||||
absolute = os.path.join(self.absolute, src)
|
||||
assert not re.search(r'[\?|><:/*]"', file) # restrictions on long names
|
||||
if self.keyfiles.has_key(file):
|
||||
logical = self.keyfiles[file]
|
||||
else:
|
||||
logical = None
|
||||
sequence, logical = self.cab.append(absolute, file, logical)
|
||||
assert logical not in self.ids
|
||||
self.ids.add(logical)
|
||||
short = self.make_short(file)
|
||||
full = "%s|%s" % (short, file)
|
||||
filesize = os.stat(absolute).st_size
|
||||
# constants.msidbFileAttributesVital
|
||||
# Compressed omitted, since it is the database default
|
||||
# could add r/o, system, hidden
|
||||
attributes = 512
|
||||
add_data(self.db, "File",
|
||||
[(logical, self.component, full, filesize, version,
|
||||
language, attributes, sequence)])
|
||||
#if not version:
|
||||
# # Add hash if the file is not versioned
|
||||
# filehash = FileHash(absolute, 0)
|
||||
# add_data(self.db, "MsiFileHash",
|
||||
# [(logical, 0, filehash.IntegerData(1),
|
||||
# filehash.IntegerData(2), filehash.IntegerData(3),
|
||||
# filehash.IntegerData(4))])
|
||||
# Automatically remove .pyc/.pyo files on uninstall (2)
|
||||
# XXX: adding so many RemoveFile entries makes installer unbelievably
|
||||
# slow. So instead, we have to use wildcard remove entries
|
||||
if file.endswith(".py"):
|
||||
add_data(self.db, "RemoveFile",
|
||||
[(logical+"c", self.component, "%sC|%sc" % (short, file),
|
||||
self.logical, 2),
|
||||
(logical+"o", self.component, "%sO|%so" % (short, file),
|
||||
self.logical, 2)])
|
||||
return logical
|
||||
|
||||
def glob(self, pattern, exclude = None):
|
||||
"""Add a list of files to the current component as specified in the
|
||||
glob pattern. Individual files can be excluded in the exclude list."""
|
||||
files = glob.glob1(self.absolute, pattern)
|
||||
for f in files:
|
||||
if exclude and f in exclude: continue
|
||||
self.add_file(f)
|
||||
return files
|
||||
|
||||
def remove_pyc(self):
|
||||
"Remove .pyc/.pyo files on uninstall"
|
||||
add_data(self.db, "RemoveFile",
|
||||
[(self.component+"c", self.component, "*.pyc", self.logical, 2),
|
||||
(self.component+"o", self.component, "*.pyo", self.logical, 2)])
|
||||
|
||||
class Binary:
|
||||
def __init__(self, fname):
|
||||
self.name = fname
|
||||
def __repr__(self):
|
||||
return 'msilib.Binary(os.path.join(dirname,"%s"))' % self.name
|
||||
|
||||
class Feature:
|
||||
def __init__(self, db, id, title, desc, display, level = 1,
|
||||
parent=None, directory = None, attributes=0):
|
||||
self.id = id
|
||||
if parent:
|
||||
parent = parent.id
|
||||
add_data(db, "Feature",
|
||||
[(id, parent, title, desc, display,
|
||||
level, directory, attributes)])
|
||||
def set_current(self):
|
||||
global current_feature
|
||||
current_feature = self
|
||||
|
||||
class Control:
|
||||
def __init__(self, dlg, name):
|
||||
self.dlg = dlg
|
||||
self.name = name
|
||||
|
||||
def event(self, ev, arg, cond = "1", order = None):
|
||||
add_data(self.dlg.db, "ControlEvent",
|
||||
[(self.dlg.name, self.name, ev, arg, cond, order)])
|
||||
|
||||
def mapping(self, ev, attr):
|
||||
add_data(self.dlg.db, "EventMapping",
|
||||
[(self.dlg.name, self.name, ev, attr)])
|
||||
|
||||
def condition(self, action, condition):
|
||||
add_data(self.dlg.db, "ControlCondition",
|
||||
[(self.dlg.name, self.name, action, condition)])
|
||||
|
||||
class RadioButtonGroup(Control):
|
||||
def __init__(self, dlg, name, property):
|
||||
self.dlg = dlg
|
||||
self.name = name
|
||||
self.property = property
|
||||
self.index = 1
|
||||
|
||||
def add(self, name, x, y, w, h, text, value = None):
|
||||
if value is None:
|
||||
value = name
|
||||
add_data(self.dlg.db, "RadioButton",
|
||||
[(self.property, self.index, value,
|
||||
x, y, w, h, text, None)])
|
||||
self.index += 1
|
||||
|
||||
class Dialog:
|
||||
def __init__(self, db, name, x, y, w, h, attr, title, first, default, cancel):
|
||||
self.db = db
|
||||
self.name = name
|
||||
self.x, self.y, self.w, self.h = x,y,w,h
|
||||
add_data(db, "Dialog", [(name, x,y,w,h,attr,title,first,default,cancel)])
|
||||
|
||||
def control(self, name, type, x, y, w, h, attr, prop, text, next, help):
|
||||
add_data(self.db, "Control",
|
||||
[(self.name, name, type, x, y, w, h, attr, prop, text, next, help)])
|
||||
return Control(self, name)
|
||||
|
||||
def text(self, name, x, y, w, h, attr, text):
|
||||
return self.control(name, "Text", x, y, w, h, attr, None,
|
||||
text, None, None)
|
||||
|
||||
def bitmap(self, name, x, y, w, h, text):
|
||||
return self.control(name, "Bitmap", x, y, w, h, 1, None, text, None, None)
|
||||
|
||||
def line(self, name, x, y, w, h):
|
||||
return self.control(name, "Line", x, y, w, h, 1, None, None, None, None)
|
||||
|
||||
def pushbutton(self, name, x, y, w, h, attr, text, next):
|
||||
return self.control(name, "PushButton", x, y, w, h, attr, None, text, next, None)
|
||||
|
||||
def radiogroup(self, name, x, y, w, h, attr, prop, text, next):
|
||||
add_data(self.db, "Control",
|
||||
[(self.name, name, "RadioButtonGroup",
|
||||
x, y, w, h, attr, prop, text, next, None)])
|
||||
return RadioButtonGroup(self, name, prop)
|
||||
|
||||
def checkbox(self, name, x, y, w, h, attr, prop, text, next):
|
||||
return self.control(name, "CheckBox", x, y, w, h, attr, prop, text, next, None)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,126 @@
|
|||
AdminExecuteSequence = [
|
||||
(u'InstallInitialize', None, 1500),
|
||||
(u'InstallFinalize', None, 6600),
|
||||
(u'InstallFiles', None, 4000),
|
||||
(u'InstallAdminPackage', None, 3900),
|
||||
(u'FileCost', None, 900),
|
||||
(u'CostInitialize', None, 800),
|
||||
(u'CostFinalize', None, 1000),
|
||||
(u'InstallValidate', None, 1400),
|
||||
]
|
||||
|
||||
AdminUISequence = [
|
||||
(u'FileCost', None, 900),
|
||||
(u'CostInitialize', None, 800),
|
||||
(u'CostFinalize', None, 1000),
|
||||
(u'ExecuteAction', None, 1300),
|
||||
(u'ExitDialog', None, -1),
|
||||
(u'FatalError', None, -3),
|
||||
(u'UserExit', None, -2),
|
||||
]
|
||||
|
||||
AdvtExecuteSequence = [
|
||||
(u'InstallInitialize', None, 1500),
|
||||
(u'InstallFinalize', None, 6600),
|
||||
(u'CostInitialize', None, 800),
|
||||
(u'CostFinalize', None, 1000),
|
||||
(u'InstallValidate', None, 1400),
|
||||
(u'CreateShortcuts', None, 4500),
|
||||
(u'MsiPublishAssemblies', None, 6250),
|
||||
(u'PublishComponents', None, 6200),
|
||||
(u'PublishFeatures', None, 6300),
|
||||
(u'PublishProduct', None, 6400),
|
||||
(u'RegisterClassInfo', None, 4600),
|
||||
(u'RegisterExtensionInfo', None, 4700),
|
||||
(u'RegisterMIMEInfo', None, 4900),
|
||||
(u'RegisterProgIdInfo', None, 4800),
|
||||
]
|
||||
|
||||
InstallExecuteSequence = [
|
||||
(u'InstallInitialize', None, 1500),
|
||||
(u'InstallFinalize', None, 6600),
|
||||
(u'InstallFiles', None, 4000),
|
||||
(u'FileCost', None, 900),
|
||||
(u'CostInitialize', None, 800),
|
||||
(u'CostFinalize', None, 1000),
|
||||
(u'InstallValidate', None, 1400),
|
||||
(u'CreateShortcuts', None, 4500),
|
||||
(u'MsiPublishAssemblies', None, 6250),
|
||||
(u'PublishComponents', None, 6200),
|
||||
(u'PublishFeatures', None, 6300),
|
||||
(u'PublishProduct', None, 6400),
|
||||
(u'RegisterClassInfo', None, 4600),
|
||||
(u'RegisterExtensionInfo', None, 4700),
|
||||
(u'RegisterMIMEInfo', None, 4900),
|
||||
(u'RegisterProgIdInfo', None, 4800),
|
||||
(u'AllocateRegistrySpace', u'NOT Installed', 1550),
|
||||
(u'AppSearch', None, 400),
|
||||
(u'BindImage', None, 4300),
|
||||
(u'CCPSearch', u'NOT Installed', 500),
|
||||
(u'CreateFolders', None, 3700),
|
||||
(u'DeleteServices', u'VersionNT', 2000),
|
||||
(u'DuplicateFiles', None, 4210),
|
||||
(u'FindRelatedProducts', None, 200),
|
||||
(u'InstallODBC', None, 5400),
|
||||
(u'InstallServices', u'VersionNT', 5800),
|
||||
(u'IsolateComponents', None, 950),
|
||||
(u'LaunchConditions', None, 100),
|
||||
(u'MigrateFeatureStates', None, 1200),
|
||||
(u'MoveFiles', None, 3800),
|
||||
(u'PatchFiles', None, 4090),
|
||||
(u'ProcessComponents', None, 1600),
|
||||
(u'RegisterComPlus', None, 5700),
|
||||
(u'RegisterFonts', None, 5300),
|
||||
(u'RegisterProduct', None, 6100),
|
||||
(u'RegisterTypeLibraries', None, 5500),
|
||||
(u'RegisterUser', None, 6000),
|
||||
(u'RemoveDuplicateFiles', None, 3400),
|
||||
(u'RemoveEnvironmentStrings', None, 3300),
|
||||
(u'RemoveExistingProducts', None, 6700),
|
||||
(u'RemoveFiles', None, 3500),
|
||||
(u'RemoveFolders', None, 3600),
|
||||
(u'RemoveIniValues', None, 3100),
|
||||
(u'RemoveODBC', None, 2400),
|
||||
(u'RemoveRegistryValues', None, 2600),
|
||||
(u'RemoveShortcuts', None, 3200),
|
||||
(u'RMCCPSearch', u'NOT Installed', 600),
|
||||
(u'SelfRegModules', None, 5600),
|
||||
(u'SelfUnregModules', None, 2200),
|
||||
(u'SetODBCFolders', None, 1100),
|
||||
(u'StartServices', u'VersionNT', 5900),
|
||||
(u'StopServices', u'VersionNT', 1900),
|
||||
(u'MsiUnpublishAssemblies', None, 1750),
|
||||
(u'UnpublishComponents', None, 1700),
|
||||
(u'UnpublishFeatures', None, 1800),
|
||||
(u'UnregisterClassInfo', None, 2700),
|
||||
(u'UnregisterComPlus', None, 2100),
|
||||
(u'UnregisterExtensionInfo', None, 2800),
|
||||
(u'UnregisterFonts', None, 2500),
|
||||
(u'UnregisterMIMEInfo', None, 3000),
|
||||
(u'UnregisterProgIdInfo', None, 2900),
|
||||
(u'UnregisterTypeLibraries', None, 2300),
|
||||
(u'ValidateProductID', None, 700),
|
||||
(u'WriteEnvironmentStrings', None, 5200),
|
||||
(u'WriteIniValues', None, 5100),
|
||||
(u'WriteRegistryValues', None, 5000),
|
||||
]
|
||||
|
||||
InstallUISequence = [
|
||||
(u'FileCost', None, 900),
|
||||
(u'CostInitialize', None, 800),
|
||||
(u'CostFinalize', None, 1000),
|
||||
(u'ExecuteAction', None, 1300),
|
||||
(u'ExitDialog', None, -1),
|
||||
(u'FatalError', None, -3),
|
||||
(u'UserExit', None, -2),
|
||||
(u'AppSearch', None, 400),
|
||||
(u'CCPSearch', u'NOT Installed', 500),
|
||||
(u'FindRelatedProducts', None, 200),
|
||||
(u'IsolateComponents', None, 950),
|
||||
(u'LaunchConditions', None, 100),
|
||||
(u'MigrateFeatureStates', None, 1200),
|
||||
(u'RMCCPSearch', u'NOT Installed', 600),
|
||||
(u'ValidateProductID', None, 700),
|
||||
]
|
||||
|
||||
tables=['AdminExecuteSequence', 'AdminUISequence', 'AdvtExecuteSequence', 'InstallExecuteSequence', 'InstallUISequence']
|
File diff suppressed because it is too large
Load Diff
|
@ -425,6 +425,9 @@ Extension Modules
|
|||
Library
|
||||
-------
|
||||
|
||||
- A library msilib to generate Windows Installer files, and a distutils
|
||||
command bdist_msi have been added.
|
||||
|
||||
- PEP 343: new module contextlib.py defines decorator @contextmanager
|
||||
and helpful context managers nested() and closing().
|
||||
|
||||
|
|
|
@ -0,0 +1,252 @@
|
|||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="7.10"
|
||||
Name="_msi"
|
||||
ProjectGUID="{2C0BEFB9-70E2-4F80-AC5B-4AB8EE023574}"
|
||||
SccProjectName="_msi"
|
||||
SccLocalPath="..\pc">
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"/>
|
||||
</Platforms>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory=".\."
|
||||
IntermediateDirectory=".\x86-temp-debug\_msi"
|
||||
ConfigurationType="2"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\Include,..\PC"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="2"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="3"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="fci.lib msi.lib rpcrt4.lib"
|
||||
OutputFile="./_msi.pyd"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
GenerateDebugInformation="TRUE"
|
||||
ProgramDatabaseFile=".\./_msi.pdb"
|
||||
BaseAddress="0x1D160000"
|
||||
ImportLibrary=".\./_msi.lib"
|
||||
TargetMachine="1"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory=".\."
|
||||
IntermediateDirectory=".\x86-temp-release\_msi"
|
||||
ConfigurationType="2"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
InlineFunctionExpansion="1"
|
||||
AdditionalIncludeDirectories="..\Include,..\PC"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL"
|
||||
StringPooling="TRUE"
|
||||
RuntimeLibrary="2"
|
||||
EnableFunctionLevelLinking="TRUE"
|
||||
UsePrecompiledHeader="2"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="fci.lib msi.lib rpcrt4.lib"
|
||||
OutputFile="./_msi.pyd"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
ProgramDatabaseFile=".\./_msi.pdb"
|
||||
BaseAddress="0x1D160000"
|
||||
ImportLibrary=".\./_msi.lib"
|
||||
TargetMachine="1"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="ReleaseItanium|Win32"
|
||||
OutputDirectory=".\."
|
||||
IntermediateDirectory=".\ia64-temp-release\_msi"
|
||||
ConfigurationType="2"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalOptions=" /USECL:MS_ITANIUM"
|
||||
Optimization="2"
|
||||
InlineFunctionExpansion="1"
|
||||
AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl;{MSSDKPATH}\include\Win64\crt;{MSSDKPATH}\include\Win64\crt\sys;{MSSDKPATH}\include\Win64\mfc;..\Include,..\PC"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL"
|
||||
StringPooling="TRUE"
|
||||
BasicRuntimeChecks="0"
|
||||
RuntimeLibrary="2"
|
||||
BufferSecurityCheck="FALSE"
|
||||
EnableFunctionLevelLinking="TRUE"
|
||||
UsePrecompiledHeader="2"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
Detect64BitPortabilityProblems="TRUE"
|
||||
DebugInformationFormat="3"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalOptions=" /MACHINE:IA64 /USELINK:MS_SDK"
|
||||
AdditionalDependencies="fci.lib msi.lib rpcrt4.lib"
|
||||
OutputFile="./_msi.pyd"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
ProgramDatabaseFile=".\./_msi.pdb"
|
||||
BaseAddress="0x1D160000"
|
||||
ImportLibrary=".\./_msi.lib"
|
||||
TargetMachine="0"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="ReleaseAMD64|Win32"
|
||||
OutputDirectory="."
|
||||
IntermediateDirectory="amd64-temp-release\_msi"
|
||||
ConfigurationType="2"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalOptions=" /USECL:MS_OPTERON"
|
||||
Optimization="2"
|
||||
InlineFunctionExpansion="1"
|
||||
AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl\amd64;{MSSDKPATH}\include\Win64\crt\amd64;{MSSDKPATH}\include\Win64\crt\amd64\sys;{MSSDKPATH}\include\Win64\mfc\amd64;..\Include,..\PC"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL"
|
||||
StringPooling="TRUE"
|
||||
BasicRuntimeChecks="0"
|
||||
RuntimeLibrary="2"
|
||||
BufferSecurityCheck="FALSE"
|
||||
EnableFunctionLevelLinking="TRUE"
|
||||
UsePrecompiledHeader="2"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
Detect64BitPortabilityProblems="TRUE"
|
||||
DebugInformationFormat="3"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalOptions=" /MACHINE:AMD64 /USELINK:MS_SDK"
|
||||
AdditionalDependencies="fci.lib msi.lib rpcrt4.lib"
|
||||
OutputFile="./_msi.pyd"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
ProgramDatabaseFile=".\./_msi.pdb"
|
||||
BaseAddress="0x1D160000"
|
||||
ImportLibrary=".\./_msi.lib"
|
||||
TargetMachine="0"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<File
|
||||
RelativePath="..\PC\_msi.c">
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
|
@ -84,6 +84,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "make_buildinfo", "make_buil
|
|||
ProjectSection(ProjectDependencies) = postProject
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_msi", "_msi.vcproj", "{2C0BEFB9-70E2-4F80-AC5B-4AB8EE023574}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CF7AC3D1-E2DF-41D2-BEA6-1E2556CDEA26} = {CF7AC3D1-E2DF-41D2-BEA6-1E2556CDEA26}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfiguration) = preSolution
|
||||
Debug = Debug
|
||||
|
@ -226,6 +231,14 @@ Global
|
|||
{C73F0EC1-358B-4177-940F-0846AC8B04CD}.ReleaseAMD64.Build.0 = Release|Win32
|
||||
{C73F0EC1-358B-4177-940F-0846AC8B04CD}.ReleaseItanium.ActiveCfg = Release|Win32
|
||||
{C73F0EC1-358B-4177-940F-0846AC8B04CD}.ReleaseItanium.Build.0 = Release|Win32
|
||||
{2C0BEFB9-70E2-4F80-AC5B-4AB8EE023574}.Debug.ActiveCfg = Debug|Win32
|
||||
{2C0BEFB9-70E2-4F80-AC5B-4AB8EE023574}.Debug.Build.0 = Debug|Win32
|
||||
{2C0BEFB9-70E2-4F80-AC5B-4AB8EE023574}.Release.ActiveCfg = Release|Win32
|
||||
{2C0BEFB9-70E2-4F80-AC5B-4AB8EE023574}.Release.Build.0 = Release|Win32
|
||||
{2C0BEFB9-70E2-4F80-AC5B-4AB8EE023574}.ReleaseAMD64.ActiveCfg = ReleaseAMD64|Win32
|
||||
{2C0BEFB9-70E2-4F80-AC5B-4AB8EE023574}.ReleaseAMD64.Build.0 = ReleaseAMD64|Win32
|
||||
{2C0BEFB9-70E2-4F80-AC5B-4AB8EE023574}.ReleaseItanium.ActiveCfg = ReleaseItanium|Win32
|
||||
{2C0BEFB9-70E2-4F80-AC5B-4AB8EE023574}.ReleaseItanium.Build.0 = ReleaseItanium|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionItems) = postSolution
|
||||
..\Modules\getbuildinfo.c = ..\Modules\getbuildinfo.c
|
||||
|
|
Loading…
Reference in New Issue