| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  | # Microsoft Installer Library | 
					
						
							|  |  |  | # (C) 2003 Martin v. Loewis | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import win32com.client.gencache | 
					
						
							|  |  |  | import win32com.client | 
					
						
							|  |  |  | import pythoncom, pywintypes | 
					
						
							|  |  |  | from win32com.client import constants | 
					
						
							| 
									
										
										
										
											2010-08-28 13:39:09 +00:00
										 |  |  | import re, string, os, sets, glob, subprocess, sys, _winreg, struct, _msi | 
					
						
							| 
									
										
										
										
											2008-07-18 18:40:42 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | try: | 
					
						
							|  |  |  |     basestring | 
					
						
							|  |  |  | except NameError: | 
					
						
							|  |  |  |     basestring = (str, unicode) | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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 18:40:42 +00:00
										 |  |  |                 print "%s.%s unknown bits %x" % (self.name, name, unk) | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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 18:40:42 +00:00
										 |  |  |                 print "%s.%sunknown integer type %d" % (self.name, name, size) | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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 18:40:42 +00:00
										 |  |  |         f.write("(%s,%s,%s," % | 
					
						
							|  |  |  |                 (`r.StringData(1)`, `r.StringData(2)`, `r.StringData(3)`)) | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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 18:40:42 +00:00
										 |  |  |             else:f.write("%s," % `r.StringData(i)`) | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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 19:42:56 +00:00
										 |  |  |     f.close() | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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 18:40:42 +00:00
										 |  |  |     print >>f, "import msilib,os;dirname=os.path.dirname(__file__)" | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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 18:40:42 +00:00
										 |  |  |                     raise "Unsupported column type", info.StringData(i) | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |             f.write(repr(tuple(rec))+",\n") | 
					
						
							|  |  |  |         v1.Close() | 
					
						
							|  |  |  |         f.write("]\n\n") | 
					
						
							|  |  |  |     v.Close() | 
					
						
							| 
									
										
										
										
											2008-07-18 18:40:42 +00:00
										 |  |  |     f.write("tables=%s\n" % repr(map(str,tables))) | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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 18:40:42 +00:00
										 |  |  |     raise ValueError, "Action not found in sequence" | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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 18:40:42 +00:00
										 |  |  |             if isinstance(field, (int, long)): | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |                 r.SetIntegerData(i+1,field) | 
					
						
							| 
									
										
										
										
											2008-07-18 18:40:42 +00:00
										 |  |  |             elif isinstance(field, basestring): | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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 18:40:42 +00:00
										 |  |  |                 raise TypeError, "Unsupported type %s" % field.__class__.__name__ | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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 21:11:29 +00:00
										 |  |  |                   Manufacturer, | 
					
						
							|  |  |  |                   request_uac = False): | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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) | 
					
						
							| 
									
										
										
										
											2013-08-26 01:32:56 +03:00
										 |  |  |     # Initialize the summary information, allowing at most 20 properties | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |     si = db.GetSummaryInformation(20) | 
					
						
							|  |  |  |     si.SetProperty(PID_TITLE, "Installation Database") | 
					
						
							|  |  |  |     si.SetProperty(PID_SUBJECT, ProductName) | 
					
						
							|  |  |  |     si.SetProperty(PID_AUTHOR, Manufacturer) | 
					
						
							| 
									
										
										
										
											2006-02-14 20:42:55 +00:00
										 |  |  |     si.SetProperty(PID_TEMPLATE, msi_type) | 
					
						
							| 
									
										
										
										
											2004-09-10 11:55:32 +00: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 21:11:29 +00: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 13:34:34 +00: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 14:11:59 +00:00
										 |  |  |     str = str.replace("+", "_") | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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 | 
					
						
							| 
									
										
										
										
											2010-08-28 13:39:09 +00:00
										 |  |  |         self.files = [] | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											2010-08-28 13:39:09 +00:00
										 |  |  |         self.files.append((full, logical)) | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |         return self.index, logical | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def commit(self, db): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             os.unlink(self.name+".cab") | 
					
						
							|  |  |  |         except OSError: | 
					
						
							|  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2010-08-28 13:39:09 +00:00
										 |  |  |         _msi.FCICreate(self.name+".cab", self.files) | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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+".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 | 
					
						
							| 
									
										
										
										
											2012-02-21 18:49:10 +01:00
										 |  |  |         self.short_names = {} | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											2010-08-03 18:35:55 +00:00
										 |  |  |         # initially assume that all files in this directory are unpackaged | 
					
						
							|  |  |  |         # as files from self.absolute get added, this set is reduced | 
					
						
							|  |  |  |         self.unpackaged_files = set() | 
					
						
							|  |  |  |         for f in os.listdir(self.absolute): | 
					
						
							|  |  |  |             if os.path.isfile(os.path.join(self.absolute, f)): | 
					
						
							|  |  |  |                 self.unpackaged_files.add(f) | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |         add_data(db, "Directory", [(logical, blogical, default)]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-03-15 00:39:40 +00:00
										 |  |  |     def start_component(self, component = None, feature = None, flags = None, keyfile = None, uuid=None): | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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-15 00:39:40 +00:00
										 |  |  |         if uuid is None: | 
					
						
							|  |  |  |             uuid = gen_uuid() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             uuid = uuid.upper() | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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): | 
					
						
							| 
									
										
										
										
											2012-02-21 18:49:10 +01:00
										 |  |  |         long = file | 
					
						
							| 
									
										
										
										
											2008-06-13 14:11:59 +00:00
										 |  |  |         file = re.sub(r'[\?|><:/*"+,;=\[\]]', '_', file) # restrictions on short names | 
					
						
							| 
									
										
										
										
											2012-02-21 18:49:10 +01:00
										 |  |  |         parts = file.split(".", 1) | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |         if len(parts)>1: | 
					
						
							| 
									
										
										
										
											2012-02-21 18:49:10 +01:00
										 |  |  |             suffix = parts[1].upper() | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2012-02-21 18:49:10 +01:00
										 |  |  |             suffix = '' | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |         prefix = parts[0].upper() | 
					
						
							| 
									
										
										
										
											2012-02-21 18:49:10 +01:00
										 |  |  |         if len(prefix) <= 8 and '.' not in suffix and len(suffix) <= 3: | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |             if suffix: | 
					
						
							|  |  |  |                 file = prefix+"."+suffix | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 file = prefix | 
					
						
							| 
									
										
										
										
											2012-02-21 18:49:10 +01:00
										 |  |  |             assert file not in self.short_names, (file, self.short_names[file]) | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |         else: | 
					
						
							|  |  |  |             prefix = prefix[:6] | 
					
						
							|  |  |  |             if suffix: | 
					
						
							| 
									
										
										
										
											2012-02-21 18:49:10 +01:00
										 |  |  |                 # last three characters of last suffix | 
					
						
							|  |  |  |                 suffix = suffix.rsplit('.')[-1][:3] | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |             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] | 
					
						
							| 
									
										
										
										
											2012-02-21 18:49:10 +01:00
										 |  |  |         self.short_names[file] = long | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |         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
 | 
					
						
							| 
									
										
										
										
											2013-03-11 17:23:46 -04:00
										 |  |  |         if there is no current component. By default, the file name in the source | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |         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) | 
					
						
							| 
									
										
										
										
											2010-08-03 18:35:55 +00:00
										 |  |  |         if absolute.startswith(self.absolute): | 
					
						
							|  |  |  |             # mark file as packaged | 
					
						
							|  |  |  |             relative = absolute[len(self.absolute)+1:] | 
					
						
							|  |  |  |             if relative in self.unpackaged_files: | 
					
						
							|  |  |  |                 self.unpackaged_files.remove(relative) | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |         assert not re.search(r'[\?|><:/*]"', file) # restrictions on long names | 
					
						
							| 
									
										
										
										
											2008-07-18 18:40:42 +00:00
										 |  |  |         if self.keyfiles.has_key(file): | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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 19:42:56 +00:00
										 |  |  |         attributes = 512 | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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): | 
					
						
							| 
									
										
										
										
											2010-08-06 10:43:31 +00:00
										 |  |  |         "Remove .pyc/.pyo files from __pycache__ on uninstall" | 
					
						
							|  |  |  |         directory = self.logical + "_pycache" | 
					
						
							|  |  |  |         add_data(self.db, "Directory", [(directory, self.logical, "__PYCA~1|__pycache__")]) | 
					
						
							|  |  |  |         flags = 256 if Win64 else 0 | 
					
						
							|  |  |  |         add_data(self.db, "Component", | 
					
						
							|  |  |  |                 [(directory, gen_uuid(), directory, flags, None, None)]) | 
					
						
							|  |  |  |         add_data(self.db, "FeatureComponents", [(current_feature.id, directory)]) | 
					
						
							|  |  |  |         add_data(self.db, "CreateFolder", [(directory, directory)]) | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  |         add_data(self.db, "RemoveFile", | 
					
						
							| 
									
										
										
										
											2010-08-06 10:43:31 +00:00
										 |  |  |                  [(self.component, self.component, "*.*", directory, 2), | 
					
						
							|  |  |  |                  ]) | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-02 10:04:16 +00: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 13:34:34 +00: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 19:42:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-08-22 13:34:34 +00: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 19:42:56 +00:00
										 |  |  |         return self.control(name, "CheckBox", x, y, w, h, attr, prop, text, next, None) | 
					
						
							| 
									
										
										
										
											2006-02-14 20:42:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def pe_type(path): | 
					
						
							| 
									
										
										
										
											2006-03-05 13:52:20 +00: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 20:42:55 +00: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 18:40:42 +00:00
										 |  |  |         raise ValueError, "Unsupported architecture" | 
					
						
							| 
									
										
										
										
											2006-02-14 20:42:55 +00:00
										 |  |  |     msi_type += ";1033" |