blob: 9011b0a568b5bd973ee9ae5242c7e13b5289fd8c [file] [log] [blame]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001# Microsoft Installer Library
2# (C) 2003 Martin v. Loewis
3
4import win32com.client.gencache
5import win32com.client
6import pythoncom, pywintypes
7from win32com.client import constants
Martin v. Löwis856bf9a2006-02-14 20:42:55 +00008import re, string, os, sets, glob, popen2, sys, _winreg, struct
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00009
Martin v. Löwis104c46b2004-09-07 15:37:26 +000010try:
11 basestring
12except NameError:
13 basestring = (str, unicode)
14
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000015# Partially taken from Wine
16datasizemask= 0x00ff
17type_valid= 0x0100
18type_localizable= 0x0200
19
20typemask= 0x0c00
21type_long= 0x0000
22type_short= 0x0400
23type_string= 0x0c00
24type_binary= 0x0800
25
26type_nullable= 0x1000
27type_key= 0x2000
28# XXX temporary, localizable?
29knownbits = datasizemask | type_valid | type_localizable | \
30 typemask | type_nullable | type_key
31
32# Summary Info Property IDs
33PID_CODEPAGE=1
34PID_TITLE=2
35PID_SUBJECT=3
36PID_AUTHOR=4
37PID_KEYWORDS=5
38PID_COMMENTS=6
39PID_TEMPLATE=7
40PID_LASTAUTHOR=8
41PID_REVNUMBER=9
42PID_LASTPRINTED=11
43PID_CREATE_DTM=12
44PID_LASTSAVE_DTM=13
45PID_PAGECOUNT=14
46PID_WORDCOUNT=15
47PID_CHARCOUNT=16
48PID_APPNAME=18
49PID_SECURITY=19
50
51def reset():
52 global _directories
53 _directories = sets.Set()
54
55def EnsureMSI():
56 win32com.client.gencache.EnsureModule('{000C1092-0000-0000-C000-000000000046}', 1033, 1, 0)
57
58def EnsureMSM():
59 try:
60 win32com.client.gencache.EnsureModule('{0ADDA82F-2C26-11D2-AD65-00A0C9AF11A6}', 0, 1, 0)
61 except pywintypes.com_error:
62 win32com.client.gencache.EnsureModule('{0ADDA82F-2C26-11D2-AD65-00A0C9AF11A6}', 0, 2, 0)
63
64_Installer=None
65def MakeInstaller():
66 global _Installer
67 if _Installer is None:
68 EnsureMSI()
69 _Installer = win32com.client.Dispatch('WindowsInstaller.Installer',
70 resultCLSID='{000C1090-0000-0000-C000-000000000046}')
71 return _Installer
72
73_Merge=None
74def MakeMerge2():
75 global _Merge
76 if _Merge is None:
77 EnsureMSM()
78 _Merge = win32com.client.Dispatch("Msm.Merge2.1")
79 return _Merge
80
81class Table:
82 def __init__(self, name):
83 self.name = name
84 self.fields = []
85
86 def add_field(self, index, name, type):
87 self.fields.append((index,name,type))
88
89 def sql(self):
90 fields = []
91 keys = []
92 self.fields.sort()
93 fields = [None]*len(self.fields)
94 for index, name, type in self.fields:
95 index -= 1
96 unk = type & ~knownbits
97 if unk:
98 print "%s.%s unknown bits %x" % (self.name, name, unk)
99 size = type & datasizemask
100 dtype = type & typemask
101 if dtype == type_string:
102 if size:
103 tname="CHAR(%d)" % size
104 else:
105 tname="CHAR"
106 elif dtype == type_short:
107 assert size==2
108 tname = "SHORT"
109 elif dtype == type_long:
110 assert size==4
111 tname="LONG"
112 elif dtype == type_binary:
113 assert size==0
114 tname="OBJECT"
115 else:
116 tname="unknown"
117 print "%s.%sunknown integer type %d" % (self.name, name, size)
118 if type & type_nullable:
119 flags = ""
120 else:
121 flags = " NOT NULL"
122 if type & type_localizable:
123 flags += " LOCALIZABLE"
124 fields[index] = "`%s` %s%s" % (name, tname, flags)
125 if type & type_key:
126 keys.append("`%s`" % name)
127 fields = ", ".join(fields)
128 keys = ", ".join(keys)
129 return "CREATE TABLE %s (%s PRIMARY KEY %s)" % (self.name, fields, keys)
130
131 def create(self, db):
132 v = db.OpenView(self.sql())
133 v.Execute(None)
134 v.Close()
135
136class Binary:
137 def __init__(self, fname):
138 self.name = fname
139 def __repr__(self):
140 return 'msilib.Binary(os.path.join(dirname,"%s"))' % self.name
141
142def gen_schema(destpath, schemapath):
143 d = MakeInstaller()
144 schema = d.OpenDatabase(schemapath,
145 win32com.client.constants.msiOpenDatabaseModeReadOnly)
146
147 # XXX ORBER BY
148 v=schema.OpenView("SELECT * FROM _Columns")
149 curtable=None
150 tables = []
151 v.Execute(None)
152 f = open(destpath, "wt")
153 f.write("from msilib import Table\n")
154 while 1:
155 r=v.Fetch()
156 if not r:break
157 name=r.StringData(1)
158 if curtable != name:
159 f.write("\n%s = Table('%s')\n" % (name,name))
160 curtable = name
161 tables.append(name)
162 f.write("%s.add_field(%d,'%s',%d)\n" %
163 (name, r.IntegerData(2), r.StringData(3), r.IntegerData(4)))
164 v.Close()
165
166 f.write("\ntables=[%s]\n\n" % (", ".join(tables)))
167
168 # Fill the _Validation table
169 f.write("_Validation_records = [\n")
170 v = schema.OpenView("SELECT * FROM _Validation")
171 v.Execute(None)
172 while 1:
173 r = v.Fetch()
174 if not r:break
175 # Table, Column, Nullable
176 f.write("(%s,%s,%s," %
177 (`r.StringData(1)`, `r.StringData(2)`, `r.StringData(3)`))
178 def put_int(i):
179 if r.IsNull(i):f.write("None, ")
180 else:f.write("%d," % r.IntegerData(i))
181 def put_str(i):
182 if r.IsNull(i):f.write("None, ")
183 else:f.write("%s," % `r.StringData(i)`)
184 put_int(4) # MinValue
185 put_int(5) # MaxValue
186 put_str(6) # KeyTable
187 put_int(7) # KeyColumn
188 put_str(8) # Category
189 put_str(9) # Set
190 put_str(10)# Description
191 f.write("),\n")
192 f.write("]\n\n")
193
Tim Peters94607dd2004-08-22 19:42:56 +0000194 f.close()
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000195
196def gen_sequence(destpath, msipath):
197 dir = os.path.dirname(destpath)
198 d = MakeInstaller()
199 seqmsi = d.OpenDatabase(msipath,
200 win32com.client.constants.msiOpenDatabaseModeReadOnly)
201
202 v = seqmsi.OpenView("SELECT * FROM _Tables");
203 v.Execute(None)
204 f = open(destpath, "w")
205 print >>f, "import msilib,os;dirname=os.path.dirname(__file__)"
206 tables = []
207 while 1:
208 r = v.Fetch()
209 if not r:break
210 table = r.StringData(1)
211 tables.append(table)
212 f.write("%s = [\n" % table)
213 v1 = seqmsi.OpenView("SELECT * FROM `%s`" % table)
214 v1.Execute(None)
215 info = v1.ColumnInfo(constants.msiColumnInfoTypes)
216 while 1:
217 r = v1.Fetch()
218 if not r:break
219 rec = []
220 for i in range(1,r.FieldCount+1):
221 if r.IsNull(i):
222 rec.append(None)
223 elif info.StringData(i)[0] in "iI":
224 rec.append(r.IntegerData(i))
225 elif info.StringData(i)[0] in "slSL":
226 rec.append(r.StringData(i))
227 elif info.StringData(i)[0]=="v":
228 size = r.DataSize(i)
229 bytes = r.ReadStream(i, size, constants.msiReadStreamBytes)
230 bytes = bytes.encode("latin-1") # binary data represented "as-is"
231 if table == "Binary":
232 fname = rec[0]+".bin"
233 open(os.path.join(dir,fname),"wb").write(bytes)
234 rec.append(Binary(fname))
235 else:
236 rec.append(bytes)
237 else:
238 raise "Unsupported column type", info.StringData(i)
239 f.write(repr(tuple(rec))+",\n")
240 v1.Close()
241 f.write("]\n\n")
242 v.Close()
243 f.write("tables=%s\n" % repr(map(str,tables)))
244 f.close()
245
246class _Unspecified:pass
247def change_sequence(seq, action, seqno=_Unspecified, cond = _Unspecified):
248 "Change the sequence number of an action in a sequence list"
249 for i in range(len(seq)):
250 if seq[i][0] == action:
251 if cond is _Unspecified:
252 cond = seq[i][1]
253 if seqno is _Unspecified:
254 seqno = seq[i][2]
255 seq[i] = (action, cond, seqno)
256 return
257 raise ValueError, "Action not found in sequence"
258
259def add_data(db, table, values):
260 d = MakeInstaller()
261 v = db.OpenView("SELECT * FROM `%s`" % table)
262 count = v.ColumnInfo(0).FieldCount
263 r = d.CreateRecord(count)
264 for value in values:
265 assert len(value) == count, value
266 for i in range(count):
267 field = value[i]
268 if isinstance(field, (int, long)):
269 r.SetIntegerData(i+1,field)
270 elif isinstance(field, basestring):
271 r.SetStringData(i+1,field)
272 elif field is None:
273 pass
274 elif isinstance(field, Binary):
275 r.SetStream(i+1, field.name)
276 else:
277 raise TypeError, "Unsupported type %s" % field.__class__.__name__
278 v.Modify(win32com.client.constants.msiViewModifyInsert, r)
279 r.ClearData()
280 v.Close()
281
282def add_stream(db, name, path):
283 d = MakeInstaller()
284 v = db.OpenView("INSERT INTO _Streams (Name, Data) VALUES ('%s', ?)" % name)
285 r = d.CreateRecord(1)
286 r.SetStream(1, path)
287 v.Execute(r)
288 v.Close()
289
290def init_database(name, schema,
291 ProductName, ProductCode, ProductVersion,
292 Manufacturer):
293 try:
294 os.unlink(name)
295 except OSError:
296 pass
297 ProductCode = ProductCode.upper()
298 d = MakeInstaller()
299 # Create the database
300 db = d.OpenDatabase(name,
301 win32com.client.constants.msiOpenDatabaseModeCreate)
302 # Create the tables
303 for t in schema.tables:
304 t.create(db)
305 # Fill the validation table
306 add_data(db, "_Validation", schema._Validation_records)
307 # Initialize the summary information, allowing atmost 20 properties
308 si = db.GetSummaryInformation(20)
309 si.SetProperty(PID_TITLE, "Installation Database")
310 si.SetProperty(PID_SUBJECT, ProductName)
311 si.SetProperty(PID_AUTHOR, Manufacturer)
Martin v. Löwis856bf9a2006-02-14 20:42:55 +0000312 si.SetProperty(PID_TEMPLATE, msi_type)
Martin v. Löwis1e3a2642004-09-10 11:55:32 +0000313 si.SetProperty(PID_REVNUMBER, gen_uuid())
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000314 si.SetProperty(PID_WORDCOUNT, 2) # long file names, compressed, original media
315 si.SetProperty(PID_PAGECOUNT, 200)
316 si.SetProperty(PID_APPNAME, "Python MSI Library")
317 # XXX more properties
318 si.Persist()
319 add_data(db, "Property", [
320 ("ProductName", ProductName),
321 ("ProductCode", ProductCode),
322 ("ProductVersion", ProductVersion),
323 ("Manufacturer", Manufacturer),
324 ("ProductLanguage", "1033")])
325 db.Commit()
326 return db
327
328def add_tables(db, module):
329 for table in module.tables:
330 add_data(db, table, getattr(module, table))
331
332def make_id(str):
333 #str = str.replace(".", "_") # colons are allowed
334 str = str.replace(" ", "_")
335 str = str.replace("-", "_")
336 if str[0] in string.digits:
337 str = "_"+str
338 assert re.match("^[A-Za-z_][A-Za-z0-9_.]*$", str), "FILE"+str
339 return str
340
341def gen_uuid():
342 return str(pythoncom.CreateGuid())
343
344class CAB:
345 def __init__(self, name):
346 self.name = name
347 self.file = open(name+".txt", "wt")
348 self.filenames = sets.Set()
349 self.index = 0
350
351 def gen_id(self, dir, file):
352 logical = _logical = make_id(file)
353 pos = 1
354 while logical in self.filenames:
355 logical = "%s.%d" % (_logical, pos)
356 pos += 1
357 self.filenames.add(logical)
358 return logical
359
360 def append(self, full, file, logical = None):
361 if os.path.isdir(full):
362 return
363 if not logical:
364 logical = self.gen_id(dir, file)
365 self.index += 1
366 if full.find(" ")!=-1:
367 print >>self.file, '"%s" %s' % (full, logical)
368 else:
369 print >>self.file, '%s %s' % (full, logical)
370 return self.index, logical
371
372 def commit(self, db):
373 self.file.close()
374 try:
375 os.unlink(self.name+".cab")
376 except OSError:
377 pass
378 for k, v in [(r"Software\Microsoft\VisualStudio\7.1\Setup\VS", "VS7CommonBinDir"),
379 (r"Software\Microsoft\Win32SDK\Directories", "Install Dir")]:
380 try:
381 key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, k)
382 except WindowsError:
383 continue
384 cabarc = os.path.join(_winreg.QueryValueEx(key, v)[0], r"Bin", "cabarc.exe")
385 _winreg.CloseKey(key)
386 if not os.path.exists(cabarc):continue
387 break
388 else:
389 print "WARNING: cabarc.exe not found in registry"
390 cabarc = "cabarc.exe"
Martin v. Löwis420254d2005-09-27 19:17:03 +0000391 f = popen2.popen4(r'"%s" -m lzx:21 n %s.cab @%s.txt' % (cabarc, self.name, self.name))[0]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000392 for line in f:
393 if line.startswith(" -- adding "):
394 sys.stdout.write(".")
395 else:
396 sys.stdout.write(line)
397 sys.stdout.flush()
398 if not os.path.exists(self.name+".cab"):
399 raise IOError, "cabarc failed"
400 add_data(db, "Media",
401 [(1, self.index, None, "#"+self.name, None, None)])
402 add_stream(db, self.name, self.name+".cab")
403 os.unlink(self.name+".txt")
404 os.unlink(self.name+".cab")
405 db.Commit()
406
407_directories = sets.Set()
408class Directory:
409 def __init__(self, db, cab, basedir, physical, _logical, default, componentflags=None):
410 """Create a new directory in the Directory table. There is a current component
411 at each point in time for the directory, which is either explicitly created
412 through start_component, or implicitly when files are added for the first
413 time. Files are added into the current component, and into the cab file.
414 To create a directory, a base directory object needs to be specified (can be
415 None), the path to the physical directory, and a logical directory name.
416 Default specifies the DefaultDir slot in the directory table. componentflags
417 specifies the default flags that new components get."""
418 index = 1
419 _logical = make_id(_logical)
420 logical = _logical
421 while logical in _directories:
422 logical = "%s%d" % (_logical, index)
423 index += 1
424 _directories.add(logical)
425 self.db = db
426 self.cab = cab
427 self.basedir = basedir
428 self.physical = physical
429 self.logical = logical
430 self.component = None
431 self.short_names = sets.Set()
432 self.ids = sets.Set()
433 self.keyfiles = {}
434 self.componentflags = componentflags
435 if basedir:
436 self.absolute = os.path.join(basedir.absolute, physical)
437 blogical = basedir.logical
438 else:
439 self.absolute = physical
440 blogical = None
441 add_data(db, "Directory", [(logical, blogical, default)])
442
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000443 def start_component(self, component = None, feature = None, flags = None, keyfile = None, uuid=None):
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000444 """Add an entry to the Component table, and make this component the current for this
445 directory. If no component name is given, the directory name is used. If no feature
446 is given, the current feature is used. If no flags are given, the directory's default
447 flags are used. If no keyfile is given, the KeyPath is left null in the Component
448 table."""
449 if flags is None:
450 flags = self.componentflags
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000451 if uuid is None:
452 uuid = gen_uuid()
453 else:
454 uuid = uuid.upper()
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000455 if component is None:
456 component = self.logical
457 self.component = component
458 if Win64:
459 flags |= 256
460 if keyfile:
461 keyid = self.cab.gen_id(self.absolute, keyfile)
462 self.keyfiles[keyfile] = keyid
463 else:
464 keyid = None
465 add_data(self.db, "Component",
466 [(component, uuid, self.logical, flags, None, keyid)])
467 if feature is None:
468 feature = current_feature
469 add_data(self.db, "FeatureComponents",
470 [(feature.id, component)])
471
472 def make_short(self, file):
473 parts = file.split(".")
474 if len(parts)>1:
475 suffix = parts[-1].upper()
476 else:
477 suffix = None
478 prefix = parts[0].upper()
479 if len(prefix) <= 8 and (not suffix or len(suffix)<=3):
480 if suffix:
481 file = prefix+"."+suffix
482 else:
483 file = prefix
484 assert file not in self.short_names
485 else:
486 prefix = prefix[:6]
487 if suffix:
488 suffix = suffix[:3]
489 pos = 1
490 while 1:
491 if suffix:
492 file = "%s~%d.%s" % (prefix, pos, suffix)
493 else:
494 file = "%s~%d" % (prefix, pos)
495 if file not in self.short_names: break
496 pos += 1
497 assert pos < 10000
498 if pos in (10, 100, 1000):
499 prefix = prefix[:-1]
500 self.short_names.add(file)
501 assert not re.search(r'[\?|><:/*"+,;=\[\]]', file) # restrictions on short names
502 return file
503
504 def add_file(self, file, src=None, version=None, language=None):
505 """Add a file to the current component of the directory, starting a new one
506 one if there is no current component. By default, the file name in the source
507 and the file table will be identical. If the src file is specified, it is
508 interpreted relative to the current directory. Optionally, a version and a
509 language can be specified for the entry in the File table."""
510 if not self.component:
511 self.start_component(self.logical, current_feature)
512 if not src:
513 # Allow relative paths for file if src is not specified
514 src = file
515 file = os.path.basename(file)
516 absolute = os.path.join(self.absolute, src)
517 assert not re.search(r'[\?|><:/*]"', file) # restrictions on long names
518 if self.keyfiles.has_key(file):
519 logical = self.keyfiles[file]
520 else:
521 logical = None
522 sequence, logical = self.cab.append(absolute, file, logical)
523 assert logical not in self.ids
524 self.ids.add(logical)
525 short = self.make_short(file)
526 full = "%s|%s" % (short, file)
527 filesize = os.stat(absolute).st_size
528 # constants.msidbFileAttributesVital
529 # Compressed omitted, since it is the database default
530 # could add r/o, system, hidden
Tim Peters94607dd2004-08-22 19:42:56 +0000531 attributes = 512
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000532 add_data(self.db, "File",
533 [(logical, self.component, full, filesize, version,
534 language, attributes, sequence)])
535 if not version:
536 # Add hash if the file is not versioned
537 filehash = MakeInstaller().FileHash(absolute, 0)
538 add_data(self.db, "MsiFileHash",
539 [(logical, 0, filehash.IntegerData(1),
540 filehash.IntegerData(2), filehash.IntegerData(3),
541 filehash.IntegerData(4))])
542 # Automatically remove .pyc/.pyo files on uninstall (2)
543 # XXX: adding so many RemoveFile entries makes installer unbelievably
544 # slow. So instead, we have to use wildcard remove entries
545 # if file.endswith(".py"):
546 # add_data(self.db, "RemoveFile",
547 # [(logical+"c", self.component, "%sC|%sc" % (short, file),
548 # self.logical, 2),
549 # (logical+"o", self.component, "%sO|%so" % (short, file),
550 # self.logical, 2)])
551
552 def glob(self, pattern, exclude = None):
553 """Add a list of files to the current component as specified in the
554 glob pattern. Individual files can be excluded in the exclude list."""
555 files = glob.glob1(self.absolute, pattern)
556 for f in files:
557 if exclude and f in exclude: continue
558 self.add_file(f)
559 return files
560
561 def remove_pyc(self):
562 "Remove .pyc/.pyo files on uninstall"
563 add_data(self.db, "RemoveFile",
564 [(self.component+"c", self.component, "*.pyc", self.logical, 2),
565 (self.component+"o", self.component, "*.pyo", self.logical, 2)])
566
567class Feature:
568 def __init__(self, db, id, title, desc, display, level = 1,
569 parent=None, directory = None, attributes=0):
570 self.id = id
571 if parent:
572 parent = parent.id
573 add_data(db, "Feature",
574 [(id, parent, title, desc, display,
575 level, directory, attributes)])
576 def set_current(self):
577 global current_feature
578 current_feature = self
579
580class Control:
581 def __init__(self, dlg, name):
582 self.dlg = dlg
583 self.name = name
584
585 def event(self, ev, arg, cond = "1", order = None):
586 add_data(self.dlg.db, "ControlEvent",
587 [(self.dlg.name, self.name, ev, arg, cond, order)])
588
589 def mapping(self, ev, attr):
590 add_data(self.dlg.db, "EventMapping",
591 [(self.dlg.name, self.name, ev, attr)])
592
593 def condition(self, action, condition):
594 add_data(self.dlg.db, "ControlCondition",
595 [(self.dlg.name, self.name, action, condition)])
596
597class RadioButtonGroup(Control):
598 def __init__(self, dlg, name, property):
599 self.dlg = dlg
600 self.name = name
601 self.property = property
602 self.index = 1
603
604 def add(self, name, x, y, w, h, text, value = None):
605 if value is None:
606 value = name
607 add_data(self.dlg.db, "RadioButton",
608 [(self.property, self.index, value,
609 x, y, w, h, text, None)])
610 self.index += 1
611
612class Dialog:
613 def __init__(self, db, name, x, y, w, h, attr, title, first, default, cancel):
614 self.db = db
615 self.name = name
616 self.x, self.y, self.w, self.h = x,y,w,h
617 add_data(db, "Dialog", [(name, x,y,w,h,attr,title,first,default,cancel)])
618
619 def control(self, name, type, x, y, w, h, attr, prop, text, next, help):
620 add_data(self.db, "Control",
621 [(self.name, name, type, x, y, w, h, attr, prop, text, next, help)])
622 return Control(self, name)
623
624 def text(self, name, x, y, w, h, attr, text):
625 return self.control(name, "Text", x, y, w, h, attr, None,
626 text, None, None)
627
628 def bitmap(self, name, x, y, w, h, text):
629 return self.control(name, "Bitmap", x, y, w, h, 1, None, text, None, None)
Tim Peters94607dd2004-08-22 19:42:56 +0000630
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000631 def line(self, name, x, y, w, h):
632 return self.control(name, "Line", x, y, w, h, 1, None, None, None, None)
633
634 def pushbutton(self, name, x, y, w, h, attr, text, next):
635 return self.control(name, "PushButton", x, y, w, h, attr, None, text, next, None)
636
637 def radiogroup(self, name, x, y, w, h, attr, prop, text, next):
638 add_data(self.db, "Control",
639 [(self.name, name, "RadioButtonGroup",
640 x, y, w, h, attr, prop, text, next, None)])
641 return RadioButtonGroup(self, name, prop)
642
643 def checkbox(self, name, x, y, w, h, attr, prop, text, next):
Tim Peters94607dd2004-08-22 19:42:56 +0000644 return self.control(name, "CheckBox", x, y, w, h, attr, prop, text, next, None)
Martin v. Löwis856bf9a2006-02-14 20:42:55 +0000645
646def pe_type(path):
647 header = open(path).read(1000)
648 # offset of PE header is at offset 0x3c; 1-based
649 pe_offset = struct.unpack("<i", header[0x3c:0x40])[0]-1
650 assert header[pe_offset:pe_offset+4] == "PE\0\0"
651 machine = struct.unpack("<H", header[pe_offset+4:pe_offset+6])[0]
652 return machine
653
654def set_arch_from_file(path):
655 global msi_type, Win64, arch_ext
656 machine = pe_type(path)
657 if machine == 0x14c:
658 # i386
659 msi_type = "Intel"
660 Win64 = 0
661 arch_ext = ''
662 elif machine == 0x200:
663 # Itanium
664 msi_type = "Intel64"
665 Win64 = 1
666 arch_ext = '.ia64'
667 elif machine == 0x8664:
668 # AMD64
669 msi_type = "x64"
670 Win64 = 1
671 arch_ext = '.amd64'
672 else:
673 raise ValueError, "Unsupported architecture"
674 msi_type += ";1033"
675