ยปCore Development>Code coverage>Tools/msi/msilib.py

Python code coverage for Tools/msi/msilib.py

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