2004-08-22 10:34:34 -03:00
|
|
|
# Microsoft Installer Library
|
|
|
|
# (C) 2003 Martin v. Loewis
|
|
|
|
|
|
|
|
import win32com.client.gencache
|
|
|
|
import win32com.client
|
|
|
|
import pythoncom, pywintypes
|
|
|
|
from win32com.client import constants
|
2009-04-07 18:27:29 -03:00
|
|
|
import re, string, os, sets, glob, subprocess, sys, _winreg, struct
|
2008-07-18 15:40:42 -03:00
|
|
|
|
|
|
|
try:
|
|
|
|
basestring
|
|
|
|
except NameError:
|
|
|
|
basestring = (str, unicode)
|
2004-08-22 10:34:34 -03:00
|
|
|
|
|
|
|
# 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
|
|
|
|
|
|
|
|
# Summary Info Property IDs
|
|
|
|
PID_CODEPAGE=1
|
|
|
|
PID_TITLE=2
|
|
|
|
PID_SUBJECT=3
|
|
|
|
PID_AUTHOR=4
|
|
|
|
PID_KEYWORDS=5
|
|
|
|
PID_COMMENTS=6
|
|
|
|
PID_TEMPLATE=7
|
|
|
|
PID_LASTAUTHOR=8
|
|
|
|
PID_REVNUMBER=9
|
|
|
|
PID_LASTPRINTED=11
|
|
|
|
PID_CREATE_DTM=12
|
|
|
|
PID_LASTSAVE_DTM=13
|
|
|
|
PID_PAGECOUNT=14
|
|
|
|
PID_WORDCOUNT=15
|
|
|
|
PID_CHARCOUNT=16
|
|
|
|
PID_APPNAME=18
|
|
|
|
PID_SECURITY=19
|
|
|
|
|
|
|
|
def reset():
|
|
|
|
global _directories
|
|
|
|
_directories = sets.Set()
|
|
|
|
|
|
|
|
def EnsureMSI():
|
|
|
|
win32com.client.gencache.EnsureModule('{000C1092-0000-0000-C000-000000000046}', 1033, 1, 0)
|
|
|
|
|
|
|
|
def EnsureMSM():
|
|
|
|
try:
|
|
|
|
win32com.client.gencache.EnsureModule('{0ADDA82F-2C26-11D2-AD65-00A0C9AF11A6}', 0, 1, 0)
|
|
|
|
except pywintypes.com_error:
|
|
|
|
win32com.client.gencache.EnsureModule('{0ADDA82F-2C26-11D2-AD65-00A0C9AF11A6}', 0, 2, 0)
|
|
|
|
|
|
|
|
_Installer=None
|
|
|
|
def MakeInstaller():
|
|
|
|
global _Installer
|
|
|
|
if _Installer is None:
|
|
|
|
EnsureMSI()
|
|
|
|
_Installer = win32com.client.Dispatch('WindowsInstaller.Installer',
|
|
|
|
resultCLSID='{000C1090-0000-0000-C000-000000000046}')
|
|
|
|
return _Installer
|
|
|
|
|
|
|
|
_Merge=None
|
|
|
|
def MakeMerge2():
|
|
|
|
global _Merge
|
|
|
|
if _Merge is None:
|
|
|
|
EnsureMSM()
|
|
|
|
_Merge = win32com.client.Dispatch("Msm.Merge2.1")
|
|
|
|
return _Merge
|
|
|
|
|
|
|
|
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:
|
2008-07-18 15:40:42 -03:00
|
|
|
print "%s.%s unknown bits %x" % (self.name, name, unk)
|
2004-08-22 10:34:34 -03:00
|
|
|
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"
|
2008-07-18 15:40:42 -03:00
|
|
|
print "%s.%sunknown integer type %d" % (self.name, name, size)
|
2004-08-22 10:34:34 -03:00
|
|
|
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 Binary:
|
|
|
|
def __init__(self, fname):
|
|
|
|
self.name = fname
|
|
|
|
def __repr__(self):
|
|
|
|
return 'msilib.Binary(os.path.join(dirname,"%s"))' % self.name
|
|
|
|
|
|
|
|
def gen_schema(destpath, schemapath):
|
|
|
|
d = MakeInstaller()
|
|
|
|
schema = d.OpenDatabase(schemapath,
|
|
|
|
win32com.client.constants.msiOpenDatabaseModeReadOnly)
|
|
|
|
|
|
|
|
# XXX ORBER BY
|
|
|
|
v=schema.OpenView("SELECT * FROM _Columns")
|
|
|
|
curtable=None
|
|
|
|
tables = []
|
|
|
|
v.Execute(None)
|
|
|
|
f = open(destpath, "wt")
|
|
|
|
f.write("from msilib import Table\n")
|
|
|
|
while 1:
|
|
|
|
r=v.Fetch()
|
|
|
|
if not r:break
|
|
|
|
name=r.StringData(1)
|
|
|
|
if curtable != name:
|
|
|
|
f.write("\n%s = Table('%s')\n" % (name,name))
|
|
|
|
curtable = name
|
|
|
|
tables.append(name)
|
|
|
|
f.write("%s.add_field(%d,'%s',%d)\n" %
|
|
|
|
(name, r.IntegerData(2), r.StringData(3), r.IntegerData(4)))
|
|
|
|
v.Close()
|
|
|
|
|
|
|
|
f.write("\ntables=[%s]\n\n" % (", ".join(tables)))
|
|
|
|
|
|
|
|
# Fill the _Validation table
|
|
|
|
f.write("_Validation_records = [\n")
|
|
|
|
v = schema.OpenView("SELECT * FROM _Validation")
|
|
|
|
v.Execute(None)
|
|
|
|
while 1:
|
|
|
|
r = v.Fetch()
|
|
|
|
if not r:break
|
|
|
|
# Table, Column, Nullable
|
2008-07-18 15:40:42 -03:00
|
|
|
f.write("(%s,%s,%s," %
|
|
|
|
(`r.StringData(1)`, `r.StringData(2)`, `r.StringData(3)`))
|
2004-08-22 10:34:34 -03:00
|
|
|
def put_int(i):
|
|
|
|
if r.IsNull(i):f.write("None, ")
|
|
|
|
else:f.write("%d," % r.IntegerData(i))
|
|
|
|
def put_str(i):
|
|
|
|
if r.IsNull(i):f.write("None, ")
|
2008-07-18 15:40:42 -03:00
|
|
|
else:f.write("%s," % `r.StringData(i)`)
|
2004-08-22 10:34:34 -03:00
|
|
|
put_int(4) # MinValue
|
|
|
|
put_int(5) # MaxValue
|
|
|
|
put_str(6) # KeyTable
|
|
|
|
put_int(7) # KeyColumn
|
|
|
|
put_str(8) # Category
|
|
|
|
put_str(9) # Set
|
|
|
|
put_str(10)# Description
|
|
|
|
f.write("),\n")
|
|
|
|
f.write("]\n\n")
|
|
|
|
|
2004-08-22 16:42:56 -03:00
|
|
|
f.close()
|
2004-08-22 10:34:34 -03:00
|
|
|
|
|
|
|
def gen_sequence(destpath, msipath):
|
|
|
|
dir = os.path.dirname(destpath)
|
|
|
|
d = MakeInstaller()
|
|
|
|
seqmsi = d.OpenDatabase(msipath,
|
|
|
|
win32com.client.constants.msiOpenDatabaseModeReadOnly)
|
|
|
|
|
|
|
|
v = seqmsi.OpenView("SELECT * FROM _Tables");
|
|
|
|
v.Execute(None)
|
|
|
|
f = open(destpath, "w")
|
2008-07-18 15:40:42 -03:00
|
|
|
print >>f, "import msilib,os;dirname=os.path.dirname(__file__)"
|
2004-08-22 10:34:34 -03:00
|
|
|
tables = []
|
|
|
|
while 1:
|
|
|
|
r = v.Fetch()
|
|
|
|
if not r:break
|
|
|
|
table = r.StringData(1)
|
|
|
|
tables.append(table)
|
|
|
|
f.write("%s = [\n" % table)
|
|
|
|
v1 = seqmsi.OpenView("SELECT * FROM `%s`" % table)
|
|
|
|
v1.Execute(None)
|
|
|
|
info = v1.ColumnInfo(constants.msiColumnInfoTypes)
|
|
|
|
while 1:
|
|
|
|
r = v1.Fetch()
|
|
|
|
if not r:break
|
|
|
|
rec = []
|
|
|
|
for i in range(1,r.FieldCount+1):
|
|
|
|
if r.IsNull(i):
|
|
|
|
rec.append(None)
|
|
|
|
elif info.StringData(i)[0] in "iI":
|
|
|
|
rec.append(r.IntegerData(i))
|
|
|
|
elif info.StringData(i)[0] in "slSL":
|
|
|
|
rec.append(r.StringData(i))
|
|
|
|
elif info.StringData(i)[0]=="v":
|
|
|
|
size = r.DataSize(i)
|
|
|
|
bytes = r.ReadStream(i, size, constants.msiReadStreamBytes)
|
|
|
|
bytes = bytes.encode("latin-1") # binary data represented "as-is"
|
|
|
|
if table == "Binary":
|
|
|
|
fname = rec[0]+".bin"
|
|
|
|
open(os.path.join(dir,fname),"wb").write(bytes)
|
|
|
|
rec.append(Binary(fname))
|
|
|
|
else:
|
|
|
|
rec.append(bytes)
|
|
|
|
else:
|
2008-07-18 15:40:42 -03:00
|
|
|
raise "Unsupported column type", info.StringData(i)
|
2004-08-22 10:34:34 -03:00
|
|
|
f.write(repr(tuple(rec))+",\n")
|
|
|
|
v1.Close()
|
|
|
|
f.write("]\n\n")
|
|
|
|
v.Close()
|
2008-07-18 15:40:42 -03:00
|
|
|
f.write("tables=%s\n" % repr(map(str,tables)))
|
2004-08-22 10:34:34 -03:00
|
|
|
f.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
|
2008-07-18 15:40:42 -03:00
|
|
|
raise ValueError, "Action not found in sequence"
|
2004-08-22 10:34:34 -03:00
|
|
|
|
|
|
|
def add_data(db, table, values):
|
|
|
|
d = MakeInstaller()
|
|
|
|
v = db.OpenView("SELECT * FROM `%s`" % table)
|
|
|
|
count = v.ColumnInfo(0).FieldCount
|
|
|
|
r = d.CreateRecord(count)
|
|
|
|
for value in values:
|
|
|
|
assert len(value) == count, value
|
|
|
|
for i in range(count):
|
|
|
|
field = value[i]
|
2008-07-18 15:40:42 -03:00
|
|
|
if isinstance(field, (int, long)):
|
2004-08-22 10:34:34 -03:00
|
|
|
r.SetIntegerData(i+1,field)
|
2008-07-18 15:40:42 -03:00
|
|
|
elif isinstance(field, basestring):
|
2004-08-22 10:34:34 -03:00
|
|
|
r.SetStringData(i+1,field)
|
|
|
|
elif field is None:
|
|
|
|
pass
|
|
|
|
elif isinstance(field, Binary):
|
|
|
|
r.SetStream(i+1, field.name)
|
|
|
|
else:
|
2008-07-18 15:40:42 -03:00
|
|
|
raise TypeError, "Unsupported type %s" % field.__class__.__name__
|
2004-08-22 10:34:34 -03:00
|
|
|
v.Modify(win32com.client.constants.msiViewModifyInsert, r)
|
|
|
|
r.ClearData()
|
|
|
|
v.Close()
|
|
|
|
|
|
|
|
def add_stream(db, name, path):
|
|
|
|
d = MakeInstaller()
|
|
|
|
v = db.OpenView("INSERT INTO _Streams (Name, Data) VALUES ('%s', ?)" % name)
|
|
|
|
r = d.CreateRecord(1)
|
|
|
|
r.SetStream(1, path)
|
|
|
|
v.Execute(r)
|
|
|
|
v.Close()
|
|
|
|
|
|
|
|
def init_database(name, schema,
|
|
|
|
ProductName, ProductCode, ProductVersion,
|
Merged revisions 64119,64147,64150,64165,64219-64221,64229-64230,64233,64235,64253,64278,64280,64301,64303,64320,64328,64338-64339 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r64119 | andrew.kuchling | 2008-06-11 14:53:14 +0200 (mer., 11 juin 2008) | 1 line
Note PEP 371 section
........
r64147 | benjamin.peterson | 2008-06-11 22:04:30 +0200 (mer., 11 juin 2008) | 2 lines
update ACKS and NEWs for multiprocessing
........
r64150 | georg.brandl | 2008-06-11 22:28:06 +0200 (mer., 11 juin 2008) | 2 lines
Can we agree to put dots at entry ends? Thanks.
........
r64165 | armin.rigo | 2008-06-12 11:50:58 +0200 (jeu., 12 juin 2008) | 3 lines
Sounds obvious, but I didn't even realize that you can put non-string
keys in type dictionaries without using this locals() hack.
........
r64219 | neal.norwitz | 2008-06-13 08:00:46 +0200 (ven., 13 juin 2008) | 1 line
Check for memory alloc failure
........
r64220 | neal.norwitz | 2008-06-13 08:02:26 +0200 (ven., 13 juin 2008) | 3 lines
Fix some memory dealloc problems when exceptions occur.
It caused: "Fatal Python error: UNREF invalid object" in the DoubleTest.
........
r64221 | neal.norwitz | 2008-06-13 08:03:25 +0200 (ven., 13 juin 2008) | 3 lines
Fix typo in method name. The LT class implemented less than. The LE class
should implement less than or equal to (as the code does).
........
r64229 | georg.brandl | 2008-06-13 15:26:54 +0200 (ven., 13 juin 2008) | 2 lines
Clarification.
........
r64230 | robert.schuppenies | 2008-06-13 15:29:37 +0200 (ven., 13 juin 2008) | 2 lines
Fixed: sys.getsizeof does not take the actual length of the tuples into account.
........
r64233 | benjamin.peterson | 2008-06-13 17:11:50 +0200 (ven., 13 juin 2008) | 2 lines
platform.uname now tries to fill empty values even when os.uname is present
........
r64235 | benjamin.peterson | 2008-06-13 17:41:09 +0200 (ven., 13 juin 2008) | 1 line
set svn:ignore on multiprocessing
........
r64253 | andrew.kuchling | 2008-06-13 21:38:18 +0200 (ven., 13 juin 2008) | 1 line
Typo fixes
........
r64278 | martin.v.loewis | 2008-06-14 16:24:47 +0200 (sam., 14 juin 2008) | 2 lines
Disable UAC by default.
........
r64280 | gregory.p.smith | 2008-06-14 19:34:09 +0200 (sam., 14 juin 2008) | 3 lines
silence the test when it is skipped on some platforms. should fix a
buildbot.
........
r64301 | georg.brandl | 2008-06-15 21:54:36 +0200 (dim., 15 juin 2008) | 2 lines
Forward-port new test from r64300.
........
r64303 | raymond.hettinger | 2008-06-16 03:42:40 +0200 (lun., 16 juin 2008) | 1 line
Issue 3116: fix quadratic behavior in marshal.dumps().
........
r64320 | georg.brandl | 2008-06-16 23:00:47 +0200 (lun., 16 juin 2008) | 2 lines
Add Jesse Noller to the developers list.
........
r64328 | georg.brandl | 2008-06-17 11:01:35 +0200 (mar., 17 juin 2008) | 2 lines
Split the HTML index.
........
r64338 | vinay.sajip | 2008-06-17 13:02:14 +0200 (mar., 17 juin 2008) | 1 line
Bug #3126: StreamHandler and FileHandler check before calling "flush" and "close" that the stream object has these, using hasattr (thanks to bobf for the patch).
........
r64339 | vinay.sajip | 2008-06-17 13:04:02 +0200 (mar., 17 juin 2008) | 1 line
Updated with fix for #3126.
........
2008-06-17 18:11:29 -03:00
|
|
|
Manufacturer,
|
|
|
|
request_uac = False):
|
2004-08-22 10:34:34 -03:00
|
|
|
try:
|
|
|
|
os.unlink(name)
|
|
|
|
except OSError:
|
|
|
|
pass
|
|
|
|
ProductCode = ProductCode.upper()
|
|
|
|
d = MakeInstaller()
|
|
|
|
# Create the database
|
|
|
|
db = d.OpenDatabase(name,
|
|
|
|
win32com.client.constants.msiOpenDatabaseModeCreate)
|
|
|
|
# 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)
|
2006-02-14 16:42:55 -04:00
|
|
|
si.SetProperty(PID_TEMPLATE, msi_type)
|
2004-09-10 08:55:32 -03:00
|
|
|
si.SetProperty(PID_REVNUMBER, gen_uuid())
|
Merged revisions 64119,64147,64150,64165,64219-64221,64229-64230,64233,64235,64253,64278,64280,64301,64303,64320,64328,64338-64339 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r64119 | andrew.kuchling | 2008-06-11 14:53:14 +0200 (mer., 11 juin 2008) | 1 line
Note PEP 371 section
........
r64147 | benjamin.peterson | 2008-06-11 22:04:30 +0200 (mer., 11 juin 2008) | 2 lines
update ACKS and NEWs for multiprocessing
........
r64150 | georg.brandl | 2008-06-11 22:28:06 +0200 (mer., 11 juin 2008) | 2 lines
Can we agree to put dots at entry ends? Thanks.
........
r64165 | armin.rigo | 2008-06-12 11:50:58 +0200 (jeu., 12 juin 2008) | 3 lines
Sounds obvious, but I didn't even realize that you can put non-string
keys in type dictionaries without using this locals() hack.
........
r64219 | neal.norwitz | 2008-06-13 08:00:46 +0200 (ven., 13 juin 2008) | 1 line
Check for memory alloc failure
........
r64220 | neal.norwitz | 2008-06-13 08:02:26 +0200 (ven., 13 juin 2008) | 3 lines
Fix some memory dealloc problems when exceptions occur.
It caused: "Fatal Python error: UNREF invalid object" in the DoubleTest.
........
r64221 | neal.norwitz | 2008-06-13 08:03:25 +0200 (ven., 13 juin 2008) | 3 lines
Fix typo in method name. The LT class implemented less than. The LE class
should implement less than or equal to (as the code does).
........
r64229 | georg.brandl | 2008-06-13 15:26:54 +0200 (ven., 13 juin 2008) | 2 lines
Clarification.
........
r64230 | robert.schuppenies | 2008-06-13 15:29:37 +0200 (ven., 13 juin 2008) | 2 lines
Fixed: sys.getsizeof does not take the actual length of the tuples into account.
........
r64233 | benjamin.peterson | 2008-06-13 17:11:50 +0200 (ven., 13 juin 2008) | 2 lines
platform.uname now tries to fill empty values even when os.uname is present
........
r64235 | benjamin.peterson | 2008-06-13 17:41:09 +0200 (ven., 13 juin 2008) | 1 line
set svn:ignore on multiprocessing
........
r64253 | andrew.kuchling | 2008-06-13 21:38:18 +0200 (ven., 13 juin 2008) | 1 line
Typo fixes
........
r64278 | martin.v.loewis | 2008-06-14 16:24:47 +0200 (sam., 14 juin 2008) | 2 lines
Disable UAC by default.
........
r64280 | gregory.p.smith | 2008-06-14 19:34:09 +0200 (sam., 14 juin 2008) | 3 lines
silence the test when it is skipped on some platforms. should fix a
buildbot.
........
r64301 | georg.brandl | 2008-06-15 21:54:36 +0200 (dim., 15 juin 2008) | 2 lines
Forward-port new test from r64300.
........
r64303 | raymond.hettinger | 2008-06-16 03:42:40 +0200 (lun., 16 juin 2008) | 1 line
Issue 3116: fix quadratic behavior in marshal.dumps().
........
r64320 | georg.brandl | 2008-06-16 23:00:47 +0200 (lun., 16 juin 2008) | 2 lines
Add Jesse Noller to the developers list.
........
r64328 | georg.brandl | 2008-06-17 11:01:35 +0200 (mar., 17 juin 2008) | 2 lines
Split the HTML index.
........
r64338 | vinay.sajip | 2008-06-17 13:02:14 +0200 (mar., 17 juin 2008) | 1 line
Bug #3126: StreamHandler and FileHandler check before calling "flush" and "close" that the stream object has these, using hasattr (thanks to bobf for the patch).
........
r64339 | vinay.sajip | 2008-06-17 13:04:02 +0200 (mar., 17 juin 2008) | 1 line
Updated with fix for #3126.
........
2008-06-17 18:11:29 -03:00
|
|
|
if request_uac:
|
|
|
|
wc = 2 # long file names, compressed, original media
|
|
|
|
else:
|
|
|
|
wc = 2 | 8 # +never invoke UAC
|
|
|
|
si.SetProperty(PID_WORDCOUNT, wc)
|
2004-08-22 10:34:34 -03:00
|
|
|
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("-", "_")
|
2008-06-13 11:11:59 -03:00
|
|
|
str = str.replace("+", "_")
|
2004-08-22 10:34:34 -03:00
|
|
|
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 str(pythoncom.CreateGuid())
|
|
|
|
|
|
|
|
class CAB:
|
|
|
|
def __init__(self, name):
|
|
|
|
self.name = name
|
|
|
|
self.file = open(name+".txt", "wt")
|
|
|
|
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
|
|
|
|
if full.find(" ")!=-1:
|
2008-07-18 15:40:42 -03:00
|
|
|
print >>self.file, '"%s" %s' % (full, logical)
|
2004-08-22 10:34:34 -03:00
|
|
|
else:
|
2008-07-18 15:40:42 -03:00
|
|
|
print >>self.file, '%s %s' % (full, logical)
|
2004-08-22 10:34:34 -03:00
|
|
|
return self.index, logical
|
|
|
|
|
|
|
|
def commit(self, db):
|
|
|
|
self.file.close()
|
|
|
|
try:
|
|
|
|
os.unlink(self.name+".cab")
|
|
|
|
except OSError:
|
|
|
|
pass
|
|
|
|
for k, v in [(r"Software\Microsoft\VisualStudio\7.1\Setup\VS", "VS7CommonBinDir"),
|
2007-12-04 11:54:13 -04:00
|
|
|
(r"Software\Microsoft\VisualStudio\8.0\Setup\VS", "VS7CommonBinDir"),
|
|
|
|
(r"Software\Microsoft\VisualStudio\9.0\Setup\VS", "VS7CommonBinDir"),
|
|
|
|
(r"Software\Microsoft\Win32SDK\Directories", "Install Dir"),
|
|
|
|
]:
|
2004-08-22 10:34:34 -03:00
|
|
|
try:
|
2009-04-07 18:27:29 -03:00
|
|
|
key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, k)
|
|
|
|
dir = _winreg.QueryValueEx(key, v)[0]
|
|
|
|
_winreg.CloseKey(key)
|
2007-12-04 11:54:13 -04:00
|
|
|
except (WindowsError, IndexError):
|
|
|
|
continue
|
|
|
|
cabarc = os.path.join(dir, r"Bin", "cabarc.exe")
|
|
|
|
if not os.path.exists(cabarc):
|
2004-08-22 10:34:34 -03:00
|
|
|
continue
|
|
|
|
break
|
|
|
|
else:
|
2008-07-18 15:40:42 -03:00
|
|
|
print "WARNING: cabarc.exe not found in registry"
|
2004-08-22 10:34:34 -03:00
|
|
|
cabarc = "cabarc.exe"
|
Merged revisions 55325-55327 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/branches/p3yk
................
r55326 | guido.van.rossum | 2007-05-14 15:07:35 -0700 (Mon, 14 May 2007) | 2 lines
Don't use err.message, use err.args[0].
................
r55327 | guido.van.rossum | 2007-05-14 15:11:37 -0700 (Mon, 14 May 2007) | 259 lines
Merged revisions 54988-55226,55228-55323 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r54995 | neal.norwitz | 2007-04-26 23:45:32 -0700 (Thu, 26 Apr 2007) | 3 lines
This gets the test working on Solaris. It seems a little hokey to me,
but the test passed on Linux and Solaris, hopefully other platforms too.
........
r55002 | georg.brandl | 2007-04-27 12:20:00 -0700 (Fri, 27 Apr 2007) | 2 lines
Version fix (bug #1708710)
........
r55021 | neal.norwitz | 2007-04-29 16:53:24 -0700 (Sun, 29 Apr 2007) | 1 line
There really are some tests that are problematic.
........
r55024 | kristjan.jonsson | 2007-04-30 08:17:46 -0700 (Mon, 30 Apr 2007) | 1 line
Complete revamp of PCBuild8 directory. Use subdirectories for each project under the main pcbuild solution. Now make extensive use of property sheets to simplify project configuration. x64 build fully supported, and the process for building PGO version (Profiler Guided Optimization) simplified. All projects are now present, except _ssl, which needs to be reimplemented. Also, some of the projects that require external libraries need extra work to fully compile on x64.
........
r55025 | thomas.heller | 2007-04-30 08:44:17 -0700 (Mon, 30 Apr 2007) | 4 lines
Make sure to call PyErr_NoMemory() in several places where
PyMem_Malloc() could potentially fail.
Will backport to the release25-maint branch.
........
r55027 | thomas.heller | 2007-04-30 09:04:57 -0700 (Mon, 30 Apr 2007) | 8 lines
When accessing the .value attribute of a c_wchar_p instance, and the
instance does not point to a valid wchar_t zero-terminated string,
raise a ValueError. c_char_p does this already.
The ValueError message now contains the correct pointer address.
Will backport to release25-maint.
........
r55036 | georg.brandl | 2007-04-30 23:04:11 -0700 (Mon, 30 Apr 2007) | 2 lines
Bug #1710295: exceptions are now new-style classes.
........
r55038 | georg.brandl | 2007-04-30 23:08:15 -0700 (Mon, 30 Apr 2007) | 2 lines
Patch #1710352: add missing public functions to locale.__all__.
........
r55041 | vinay.sajip | 2007-05-01 03:20:03 -0700 (Tue, 01 May 2007) | 1 line
Added new optional credentials argument to SMTPHandler.__init__, and smtp.login() is now called in SMTPHandler.emit() if credentials are specified.
........
r55042 | vinay.sajip | 2007-05-01 03:21:45 -0700 (Tue, 01 May 2007) | 1 line
Added documentation for new optional credentials argument to SMTPHandler.__init__().
........
r55070 | neal.norwitz | 2007-05-01 21:47:55 -0700 (Tue, 01 May 2007) | 3 lines
Stop using PyMem_FREE while the GIL is not held. For details see:
http://mail.python.org/pipermail/python-dev/2007-May/072896.html
........
r55080 | armin.rigo | 2007-05-02 12:23:31 -0700 (Wed, 02 May 2007) | 6 lines
Fix for #1303614 and #1174712:
- __dict__ descriptor abuse for subclasses of built-in types
- subclassing from both ModuleType and another built-in types
Thanks zseil for the patch.
........
r55083 | georg.brandl | 2007-05-02 13:02:29 -0700 (Wed, 02 May 2007) | 3 lines
Actually raise an exception before calling ast_error_finish.
Triggers an assertion otherwise.
........
r55087 | neal.norwitz | 2007-05-02 23:47:18 -0700 (Wed, 02 May 2007) | 1 line
Handle a couple of uncaught errors. This should be backported
........
r55090 | neal.norwitz | 2007-05-03 00:20:57 -0700 (Thu, 03 May 2007) | 4 lines
Remove dead code. This code couldn't be reached because earlier in
the function there is another check for z != Py_None.
........
r55092 | thomas.heller | 2007-05-03 05:02:08 -0700 (Thu, 03 May 2007) | 1 line
Fix building _ctypes.pyd for x64 / Windows.
........
r55093 | thomas.heller | 2007-05-03 05:05:20 -0700 (Thu, 03 May 2007) | 1 line
Don't truncate pointers to integers (on win64 platform).
........
r55094 | walter.doerwald | 2007-05-03 08:13:55 -0700 (Thu, 03 May 2007) | 3 lines
Clarify the behaviour of PyUnicode_DecodeUTF16(): A BOM is only skipped
in native order mode, and only if it's the first two bytes.
........
r55101 | kristjan.jonsson | 2007-05-03 13:04:53 -0700 (Thu, 03 May 2007) | 2 lines
Fix pcbuild8 after recent overhaul: Added the version resource to python26.dll. Adjust stacksize to 2Mb and made large address aware for 32 bits, and set stacksize to 3Mb for 64 bits.
Todo: Set .dll optimized load addresses, and side-by-side packaging of the python26.dll.
........
r55102 | kristjan.jonsson | 2007-05-03 13:09:56 -0700 (Thu, 03 May 2007) | 1 line
Fix those parts in the testsuite that assumed that sys.maxint would cause overflow on x64. Now the testsuite is well behaved on that platform.
........
r55103 | kristjan.jonsson | 2007-05-03 13:27:03 -0700 (Thu, 03 May 2007) | 11 lines
Fix problems in x64 build that were discovered by the testsuite:
- Reenable modules on x64 that had been disabled aeons ago for Itanium.
- Cleared up confusion about compilers for 64 bit windows. There is only Itanium and x64. Added macros MS_WINI64 and MS_WINX64 for those rare cases where it matters, such as the disabling of modules above.
- Set target platform (_WIN32_WINNT and WINVER) to 0x0501 (XP) for x64, and 0x0400 (NT 4.0) otherwise, which are the targeted minimum platforms.
- Fixed thread_nt.h. The emulated InterlockedCompareExchange function didn?\194?\180t work on x64, probaby due to the lack of a "volatile" specifier. Anyway, win95 is no longer a target platform.
- Itertools module used wrong constant to check for overflow in count()
- PyInt_AsSsize_t couldn't deal with attribute error when accessing the __long__ member.
- PyLong_FromSsize_t() incorrectly specified that the operand were unsigned.
With these changes, the x64 passes the testsuite, for those modules present.
........
r55107 | kristjan.jonsson | 2007-05-03 17:25:08 -0700 (Thu, 03 May 2007) | 1 line
Revert compiler comment to AMD64 for x64/AMD64 builds.
........
r55115 | thomas.heller | 2007-05-04 00:14:39 -0700 (Fri, 04 May 2007) | 4 lines
Fix some ctypes test crashes, when running with a debug Python
version on win64 by using proper argtypes and restype function
attributes.
........
r55117 | thomas.heller | 2007-05-04 01:20:41 -0700 (Fri, 04 May 2007) | 4 lines
On 64-bit Windows, ffi_arg must be 8 bytes long. This fixes the
remaining crashes in the ctypes tests, when functions return float or
double types.
........
r55120 | kristjan.jonsson | 2007-05-04 08:48:15 -0700 (Fri, 04 May 2007) | 1 line
Update the pcbuild8 solution. Straightened out the _ctypes project by using a .vsproj file and a masm64.rules file to avoid redundancy
........
r55121 | kristjan.jonsson | 2007-05-04 10:28:06 -0700 (Fri, 04 May 2007) | 1 line
Minor fix of PCBuild8/_ctypes vcproj, moving include dir into the .vsprops file.
........
r55129 | thomas.heller | 2007-05-04 12:54:22 -0700 (Fri, 04 May 2007) | 3 lines
Do not truncate 64-bit pointers to 32-bit integers.
Fixes SF #1703286, will backport to release25-maint.
........
r55131 | thomas.heller | 2007-05-04 12:56:32 -0700 (Fri, 04 May 2007) | 1 line
Oops, these tests do not run on Windows CE.
........
r55140 | brett.cannon | 2007-05-04 18:34:02 -0700 (Fri, 04 May 2007) | 2 lines
Deprecate BaseException.message as per PEP 352.
........
r55154 | georg.brandl | 2007-05-05 11:55:37 -0700 (Sat, 05 May 2007) | 2 lines
Bug #1713535: typo in logging example.
........
r55158 | vinay.sajip | 2007-05-06 10:53:37 -0700 (Sun, 06 May 2007) | 1 line
Updates of recent changes to logging.
........
r55165 | neal.norwitz | 2007-05-07 00:02:26 -0700 (Mon, 07 May 2007) | 1 line
Verify changes to the trunk go to the normal checkins list
........
r55169 | kristjan.jonsson | 2007-05-07 09:46:54 -0700 (Mon, 07 May 2007) | 1 line
As per Armin Rigo's suggestion, remove special handing from intobject.c to deal with the peculiarities of classobject's implementation of the number protocol. The nb_long method of classobject now falls back to nb_int if there is no __long__ attribute present.
........
r55197 | collin.winter | 2007-05-08 21:14:36 -0700 (Tue, 08 May 2007) | 9 lines
Fix a bug in test.test_support.open_urlresource().
If the call to requires() doesn't precede the filesystem check, we get the following situation:
1. ./python Lib/test/regrtest.py test_foo # test needs urlfetch, not enabled, so skipped
2. ./python Lib/test/regrtest.py -u urlfetch test_foo # test runs
3. ./python Lib/test/regrtest.py test_foo # test runs (!)
By moving the call to requires() *before* the filesystem check, the fact that fetched files are cached on the local disk becomes an implementation detail, rather than a semantics-changing point of note.
........
r55198 | neal.norwitz | 2007-05-08 23:43:15 -0700 (Tue, 08 May 2007) | 1 line
Add markup for True/False. Will backport
........
r55205 | walter.doerwald | 2007-05-09 11:10:47 -0700 (Wed, 09 May 2007) | 4 lines
Backport checkin:
Fix a segfault when b"" was passed to b2a_qp() -- it was using strchr()
instead of memchr().
........
r55241 | neal.norwitz | 2007-05-10 22:55:15 -0700 (Thu, 10 May 2007) | 6 lines
Don't ever report a failure when the sum of the reference count differences
are zero. This should help reduce the false positives.
The message about references leaking is maintained to provide as much
info as possible rather than simply suppressing the message at the source.
........
r55242 | neal.norwitz | 2007-05-10 23:23:01 -0700 (Thu, 10 May 2007) | 1 line
Fix typo in docstring (the module is popen2, not 3).
........
r55244 | neal.norwitz | 2007-05-10 23:56:52 -0700 (Thu, 10 May 2007) | 1 line
Remove trailing whitespace in docstring
........
r55245 | neal.norwitz | 2007-05-10 23:57:33 -0700 (Thu, 10 May 2007) | 1 line
Deprecate os.popen* and popen2 module in favor of the subprocess module.
........
r55247 | neal.norwitz | 2007-05-11 00:13:30 -0700 (Fri, 11 May 2007) | 1 line
Deprecate os.popen* and popen2 module in favor of the subprocess module. (forgot the doc)
........
r55253 | georg.brandl | 2007-05-11 02:41:37 -0700 (Fri, 11 May 2007) | 3 lines
Remove an XXX that is unnecessary.
........
r55258 | georg.brandl | 2007-05-11 04:04:26 -0700 (Fri, 11 May 2007) | 2 lines
Patch #1714700: clarify os.linesep vs. tfiles opened in text mode.
(backport)
........
r55259 | georg.brandl | 2007-05-11 04:43:56 -0700 (Fri, 11 May 2007) | 2 lines
Update DDJ link.
........
r55273 | raymond.hettinger | 2007-05-11 10:59:59 -0700 (Fri, 11 May 2007) | 1 line
Better tests for posixpath.commonprefix
........
r55287 | georg.brandl | 2007-05-12 14:06:41 -0700 (Sat, 12 May 2007) | 2 lines
Bug #1046945: document SWIG options of distutils.
........
r55290 | georg.brandl | 2007-05-13 01:04:07 -0700 (Sun, 13 May 2007) | 2 lines
Add bz2 to content encodings.
........
r55297 | neal.norwitz | 2007-05-13 13:45:05 -0700 (Sun, 13 May 2007) | 3 lines
Remove Amoeba doc which was removed in version 1.0! according to Misc/HISTORY.
Hopefully Guido won't shed a tear. :-)
........
r55298 | neal.norwitz | 2007-05-13 13:54:19 -0700 (Sun, 13 May 2007) | 1 line
Remove references to stdwin which was removed long ago.
........
r55299 | neal.norwitz | 2007-05-13 14:13:42 -0700 (Sun, 13 May 2007) | 3 lines
Remove support for freebsd[23] which haven't been released since 2000
or earlier. http://www.freebsd.org/releases/index.html
........
r55320 | raymond.hettinger | 2007-05-14 13:52:31 -0700 (Mon, 14 May 2007) | 1 line
Small speedup.
........
................
2007-05-14 19:51:27 -03:00
|
|
|
cmd = r'"%s" -m lzx:21 n %s.cab @%s.txt' % (cabarc, self.name, self.name)
|
|
|
|
p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
|
2007-08-30 12:23:04 -03:00
|
|
|
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
|
|
for line in p.stdout:
|
2004-08-22 10:34:34 -03:00
|
|
|
if line.startswith(" -- adding "):
|
|
|
|
sys.stdout.write(".")
|
|
|
|
else:
|
|
|
|
sys.stdout.write(line)
|
|
|
|
sys.stdout.flush()
|
|
|
|
if not os.path.exists(self.name+".cab"):
|
2008-07-18 15:40:42 -03:00
|
|
|
raise IOError, "cabarc failed"
|
2004-08-22 10:34:34 -03:00
|
|
|
add_data(db, "Media",
|
|
|
|
[(1, self.index, None, "#"+self.name, None, None)])
|
|
|
|
add_stream(db, self.name, self.name+".cab")
|
|
|
|
os.unlink(self.name+".txt")
|
|
|
|
os.unlink(self.name+".cab")
|
|
|
|
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)])
|
|
|
|
|
2005-03-14 20:39:40 -04:00
|
|
|
def start_component(self, component = None, feature = None, flags = None, keyfile = None, uuid=None):
|
2004-08-22 10:34:34 -03:00
|
|
|
"""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
|
2005-03-14 20:39:40 -04:00
|
|
|
if uuid is None:
|
|
|
|
uuid = gen_uuid()
|
|
|
|
else:
|
|
|
|
uuid = uuid.upper()
|
2004-08-22 10:34:34 -03:00
|
|
|
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):
|
2008-06-13 11:11:59 -03:00
|
|
|
file = re.sub(r'[\?|><:/*"+,;=\[\]]', '_', file) # restrictions on short names
|
2004-08-22 10:34:34 -03:00
|
|
|
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)
|
|
|
|
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)
|
|
|
|
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
|
2008-07-18 15:40:42 -03:00
|
|
|
if self.keyfiles.has_key(file):
|
2004-08-22 10:34:34 -03:00
|
|
|
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
|
2004-08-22 16:42:56 -03:00
|
|
|
attributes = 512
|
2004-08-22 10:34:34 -03:00
|
|
|
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 = MakeInstaller().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)])
|
|
|
|
|
|
|
|
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)])
|
|
|
|
|
2008-06-02 07:04:16 -03:00
|
|
|
def removefile(self, key, pattern):
|
|
|
|
"Add a RemoveFile entry"
|
|
|
|
add_data(self.db, "RemoveFile", [(self.component+key, self.component, pattern, self.logical, 2)])
|
|
|
|
|
|
|
|
|
2004-08-22 10:34:34 -03:00
|
|
|
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)
|
2004-08-22 16:42:56 -03:00
|
|
|
|
2004-08-22 10:34:34 -03:00
|
|
|
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):
|
2004-08-22 16:42:56 -03:00
|
|
|
return self.control(name, "CheckBox", x, y, w, h, attr, prop, text, next, None)
|
2006-02-14 16:42:55 -04:00
|
|
|
|
|
|
|
def pe_type(path):
|
2006-03-05 09:52:20 -04:00
|
|
|
header = open(path, "rb").read(1000)
|
|
|
|
# offset of PE header is at offset 0x3c
|
|
|
|
pe_offset = struct.unpack("<i", header[0x3c:0x40])[0]
|
2006-02-14 16:42:55 -04:00
|
|
|
assert header[pe_offset:pe_offset+4] == "PE\0\0"
|
|
|
|
machine = struct.unpack("<H", header[pe_offset+4:pe_offset+6])[0]
|
|
|
|
return machine
|
|
|
|
|
|
|
|
def set_arch_from_file(path):
|
|
|
|
global msi_type, Win64, arch_ext
|
|
|
|
machine = pe_type(path)
|
|
|
|
if machine == 0x14c:
|
|
|
|
# i386
|
|
|
|
msi_type = "Intel"
|
|
|
|
Win64 = 0
|
|
|
|
arch_ext = ''
|
|
|
|
elif machine == 0x200:
|
|
|
|
# Itanium
|
|
|
|
msi_type = "Intel64"
|
|
|
|
Win64 = 1
|
|
|
|
arch_ext = '.ia64'
|
|
|
|
elif machine == 0x8664:
|
|
|
|
# AMD64
|
|
|
|
msi_type = "x64"
|
|
|
|
Win64 = 1
|
|
|
|
arch_ext = '.amd64'
|
|
|
|
else:
|
2008-07-18 15:40:42 -03:00
|
|
|
raise ValueError, "Unsupported architecture"
|
2006-02-14 16:42:55 -04:00
|
|
|
msi_type += ";1033"
|