1
- #!/usr/bin/python
2
-
3
1
import argparse
4
2
import string
5
3
import os
@@ -8,155 +6,12 @@ import tempfile
8
6
import shutil
9
7
import stat
10
8
11
- # includes FoundationPlist since some apps store their Info.plist
12
- # as binary PropertyLists
13
-
14
- # FoundationPlist:
15
-
16
- # Copyright 2009-2014 Greg Neagle.
17
- #
18
- # Licensed under the Apache License, Version 2.0 (the "License");
19
- # you may not use this file except in compliance with the License.
20
- # You may obtain a copy of the License at
21
- #
22
- # http://www.apache.org/licenses/LICENSE-2.0
23
- #
24
- # Unless required by applicable law or agreed to in writing, software
25
- # distributed under the License is distributed on an "AS IS" BASIS,
26
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
27
- # See the License for the specific language governing permissions and
28
- # limitations under the License.
29
- """FoundationPlist.py -- a tool to generate and parse MacOSX .plist files.
30
-
31
- This is intended as a drop-in replacement for Python's included plistlib,
32
- with a few caveats:
33
- - readPlist() and writePlist() operate only on a filepath,
34
- not a file object.
35
- - there is no support for the deprecated functions:
36
- readPlistFromResource()
37
- writePlistToResource()
38
- - there is no support for the deprecated Plist class.
39
-
40
- The Property List (.plist) file format is a simple XML pickle supporting
41
- basic object types, like dictionaries, lists, numbers and strings.
42
- Usually the top level object is a dictionary.
43
-
44
- To write out a plist file, use the writePlist(rootObject, filepath)
45
- function. 'rootObject' is the top level object, 'filepath' is a
46
- filename.
47
-
48
- To parse a plist from a file, use the readPlist(filepath) function,
49
- with a file name. It returns the top level object (again, usually a
50
- dictionary).
51
-
52
- To work with plist data in strings, you can use readPlistFromString()
53
- and writePlistToString().
54
- """
55
-
56
- from Foundation import NSData , \
57
- NSPropertyListSerialization , \
58
- NSPropertyListMutableContainersAndLeaves , \
59
- NSPropertyListXMLFormat_v1_0
60
-
61
-
62
- class FoundationPlistException (Exception ):
63
- '''Base error for this module'''
64
- pass
65
-
66
-
67
- class NSPropertyListSerializationException (FoundationPlistException ):
68
- '''Read error for this module'''
69
- pass
70
-
71
-
72
- class NSPropertyListWriteException (FoundationPlistException ):
73
- '''Write error for this module'''
74
- pass
75
-
76
-
77
- # private functions
78
- def _dataToPlist (data ):
79
- '''low-level function that parses a data object into a propertyList object'''
80
- darwin_vers = int (os .uname ()[2 ].split ('.' )[0 ])
81
- if darwin_vers > 10 :
82
- (plistObject , plistFormat , error ) = (
83
- NSPropertyListSerialization .propertyListWithData_options_format_error_ (
84
- data , NSPropertyListMutableContainersAndLeaves , None , None ))
85
- else :
86
- # 10.5 doesn't support propertyListWithData:options:format:error:
87
- # 10.6's PyObjC wrapper for propertyListWithData:options:format:error:
88
- # is broken
89
- # so use the older NSPropertyListSerialization function
90
- (plistObject , plistFormat , error ) = (
91
- NSPropertyListSerialization .propertyListFromData_mutabilityOption_format_errorDescription_ (
92
- data , NSPropertyListMutableContainersAndLeaves , None , None ))
93
- if plistObject is None :
94
- if error is None :
95
- error = "Plist data is invalid and could not be deserialized."
96
- raise NSPropertyListSerializationException (error )
97
- else :
98
- return plistObject
99
-
100
-
101
- def _plistToData (plistObject ):
102
- '''low-level function that creates NSData from a plist object'''
103
- darwin_vers = int (os .uname ()[2 ].split ('.' )[0 ])
104
- if darwin_vers > 10 :
105
- (data , error ) = (
106
- NSPropertyListSerialization .dataWithPropertyList_format_options_error_ (
107
- plistObject , NSPropertyListXMLFormat_v1_0 , 0 , None ))
108
- else :
109
- # use the older NSPropertyListSerialization function on 10.6 and 10.5
110
- (data , error ) = (
111
- NSPropertyListSerialization .dataFromPropertyList_format_errorDescription_ (
112
- plistObject , NSPropertyListXMLFormat_v1_0 , None ))
113
- if data is None :
114
- if error is None :
115
- error = "Property list invalid for format."
116
- raise NSPropertyListSerializationException (error )
117
- return data
118
-
119
-
120
- # public functions
121
- def readPlist (filepath ):
122
- '''Read a .plist file from filepath. Return the unpacked root object
123
- (which is usually a dictionary).'''
124
- try :
125
- data = NSData .dataWithContentsOfFile_ (filepath )
126
- except NSPropertyListSerializationException , error :
127
- # insert filepath info into error message
128
- errmsg = (u'%s in %s' % (error , filepath ))
129
- raise NSPropertyListSerializationException (errmsg )
130
- return _dataToPlist (data )
131
-
132
-
133
- def readPlistFromString (aString ):
134
- '''Read a plist data from a string. Return the root object.'''
135
- data = buffer (aString )
136
- return _dataToPlist (data )
137
-
138
-
139
- def writePlist (plistObject , filepath ):
140
- '''Write 'plistObject' as a plist to filepath.'''
141
- plistData = _plistToData (plistObject )
142
- if plistData .writeToFile_atomically_ (filepath , True ):
143
- return
144
- else :
145
- raise NSPropertyListWriteException (
146
- u"Failed to write plist data to %s" % filepath )
147
-
148
-
149
- def writePlistToString (plistObject ):
150
- '''Create a plist-formatted string from plistObject.'''
151
- return str (_plistToData (plistObject ))
152
-
153
-
154
9
#
155
10
# quickpkg
156
11
#
157
12
158
13
159
- quickpkg_version = '0.9 '
14
+ quickpkg_version = '1 '
160
15
supported_extensions = ['dmg', 'app', 'zip', 'xip']
161
16
162
17
@@ -165,7 +20,7 @@ supported_extensions = ['dmg', 'app', 'zip', 'xip']
165
20
166
21
def logger(log, v=0):
167
22
if args.verbosity >= v:
168
- print log
23
+ print( log)
169
24
170
25
171
26
def cmdexec(command, stdin=''):
@@ -211,7 +66,7 @@ def dmg_has_sla(dmgpath):
211
66
imageinfo_cmd = ['/usr/bin/hdiutil', 'imageinfo', dmgpath, '-plist']
212
67
result = cmdexec(imageinfo_cmd)
213
68
if result["return_code"] != 0:
214
- print "error getting imageinfo! %s, %s" % (result ["return_code" ], result ["stderr" ])
69
+ print( "error getting imageinfo! %s, %s" % (result["return_code"], result["stderr"]) )
215
70
return False
216
71
result_plist = result["stdout"]
217
72
imageinfo_dict = readPlistFromString(result_plist)
@@ -230,18 +85,18 @@ def attachdmg(dmgpath):
230
85
(theplist, alltext) = getFirstPlist(info_result["stdout"])
231
86
info_dict = readPlistFromString(theplist)
232
87
volpaths = []
233
- if "images" in info_dict .keys ():
88
+ if "images" in list( info_dict.keys() ):
234
89
for y in info_dict["images"]:
235
- if "image-path" in y .keys ():
90
+ if "image-path" in list( y.keys() ):
236
91
if y["image-path"] == dmgpath and os.path.samefile(y["image-path"], dmgpath):
237
92
for x in y.get("system-entities"):
238
- if "mount-point" in x .keys ():
93
+ if "mount-point" in list( x.keys() ):
239
94
volpaths.append(x["mount-point"])
240
95
dmg_was_mounted = True
241
96
return volpaths
242
97
else:
243
- print "error getting hdiutil info"
244
- print "(%d, %s)" % (info_result ["returncode" ], info_result ["stderr" ])
98
+ print( "error getting hdiutil info")
99
+ print( "(%d, %s)" % (info_result["returncode"], info_result["stderr"]) )
245
100
cleanup_and_exit(1)
246
101
247
102
attachcmd = ["/usr/bin/hdiutil",
@@ -253,7 +108,7 @@ def attachdmg(dmgpath):
253
108
"-nobrowse"]
254
109
if dmg_has_sla(dmgpath):
255
110
stdin = "Y\n"
256
- print "NOTE: Disk image %s has a license agreement!" % dmgpath
111
+ print( "NOTE: Disk image %s has a license agreement!" % dmgpath)
257
112
else:
258
113
stdin = ''
259
114
result = cmdexec(attachcmd, stdin)
@@ -269,8 +124,8 @@ def attachdmg(dmgpath):
269
124
# return the paths to mounted volume
270
125
return volpaths
271
126
else:
272
- print "error mounting disk image"
273
- print "(%d, %s)" % (result ["returncode" ], result ["stderr" ])
127
+ print( "error mounting disk image")
128
+ print( "(%d, %s)" % (result["returncode"], result["stderr"]) )
274
129
cleanup_and_exit(1)
275
130
276
131
@@ -291,15 +146,15 @@ def finditemswithextension(dirpath, item_extension):
291
146
if item_extension == 'app':
292
147
foundapps.append(os.path.join(dirpath, x))
293
148
else:
294
- print "path %s does not exist" % dirpath
149
+ print( "path %s does not exist" % dirpath)
295
150
cleanup_and_exit(1)
296
151
return foundapps
297
152
298
153
299
154
def appNameAndVersion(app_path):
300
155
info_path = os.path.join(app_path, "Contents/Info.plist")
301
156
if not os.path.exists(info_path):
302
- print "Application at path %s does not have Info.plist" % app_path
157
+ print( "Application at path %s does not have Info.plist" % app_path)
303
158
# TODO: cleanup volumes here
304
159
cleanup_and_exit(1)
305
160
info_plist = readPlist(info_path)
@@ -391,7 +246,7 @@ if __name__ == "__main__":
391
246
392
247
# is extension supported
393
248
if item_extension not in supported_extensions:
394
- print ".%s is not a supported extension!" % item_extension
249
+ print( ".%s is not a supported extension!" % item_extension)
395
250
exit(1)
396
251
397
252
foundapps = []
@@ -400,7 +255,7 @@ if __name__ == "__main__":
400
255
# if item is an app, just pass it on
401
256
if item_extension == 'app':
402
257
if not os.path.exists(item_path):
403
- print "This does not seem to be an Application!"
258
+ print( "This does not seem to be an Application!")
404
259
exit(1)
405
260
foundapps.append(item_path)
406
261
copy_app = True
@@ -421,11 +276,11 @@ if __name__ == "__main__":
421
276
moreapps = finditemswithextension(x, 'app')
422
277
foundapps.extend(moreapps)
423
278
if len(foundapps) == 0:
424
- print "Could not find an application!"
279
+ print( "Could not find an application!")
425
280
cleanup_and_exit(1)
426
281
elif len(foundapps) > 1:
427
- print "Found too many Applications! Can't decide!"
428
- print foundapps
282
+ print( "Found too many Applications! Can't decide!")
283
+ print( foundapps)
429
284
cleanup_and_exit(1)
430
285
431
286
# if item is zip, unzip to tmp location and find useful contents
@@ -434,16 +289,16 @@ if __name__ == "__main__":
434
289
unzip_cmd = ["/usr/bin/unzip", "-d", unarchive_path, item_path]
435
290
result = cmdexec(unzip_cmd)
436
291
if result["return_code"] != 0:
437
- print "An error occured while unzipping:"
438
- print "%d, %s" % (result ["return_code" ], result ["stderr" ])
292
+ print( "An error occured while unzipping:")
293
+ print( "%d, %s" % (result["return_code"], result["stderr"]) )
439
294
cleanup_and_exit(1)
440
295
foundapps = finditemswithextension(unarchive_path, 'app')
441
296
if len(foundapps) == 0:
442
- print "Could not find an application!"
297
+ print( "Could not find an application!")
443
298
cleanup_and_exit(1)
444
299
elif len(foundapps) > 1:
445
- print "Found too many Applications! Can't decide!"
446
- print foundapps
300
+ print( "Found too many Applications! Can't decide!")
301
+ print( foundapps)
447
302
cleanup_and_exit(1)
448
303
449
304
# if item is xip, extract to tmp location and find useful contents
@@ -456,16 +311,16 @@ if __name__ == "__main__":
456
311
result = cmdexec(xip_cmd)
457
312
os.chdir(cwd)
458
313
if result["return_code"] != 0:
459
- print "An error occured while expanding xip archive:"
460
- print "%d, %s" % (result ["return_code" ], result ["stderr" ])
314
+ print( "An error occured while expanding xip archive:")
315
+ print( "%d, %s" % (result["return_code"], result["stderr"]) )
461
316
cleanup_and_exit(1)
462
317
foundapps = finditemswithextension(unarchive_path, 'app')
463
318
if len(foundapps) == 0:
464
- print "Could not find an application!"
319
+ print( "Could not find an application!")
465
320
cleanup_and_exit(1)
466
321
elif len(foundapps) > 1:
467
- print "Found too many Applications! Can't decide!"
468
- print foundapps
322
+ print( "Found too many Applications! Can't decide!")
323
+ print( foundapps)
469
324
cleanup_and_exit(1)
470
325
471
326
logger("Found application: %s" % foundapps[0], 1)
@@ -497,16 +352,16 @@ if __name__ == "__main__":
497
352
498
353
logger(result["stdout"], 1)
499
354
if result["return_code"] != 0:
500
- print "Error Code: %d " % result ["return_code" ]
501
- print result ["stderr" ]
355
+ print( "Error Code: %d " % result["return_code"])
356
+ print( result["stderr"])
502
357
cleanup_and_exit(1)
503
358
504
359
if not args.relocatable:
505
360
# read and change component plist
506
361
components = readPlist(component_plist)
507
362
# component plist is an array of components
508
363
for bundle in components:
509
- if "BundleIsRelocatable" in bundle .keys ():
364
+ if "BundleIsRelocatable" in list( bundle.keys() ):
510
365
bundle["BundleIsRelocatable"] = False
511
366
writePlist(components, component_plist)
512
367
@@ -534,7 +389,7 @@ if __name__ == "__main__":
534
389
pkg_path]
535
390
536
391
if args.scripts and not os.path.exists(args.scripts):
537
- print "scripts folder %s does not exist!" % args .scripts
392
+ print( "scripts folder %s does not exist!" % args.scripts)
538
393
cleanup_and_exit(1)
539
394
540
395
if args.postinstall or args.preinstall:
@@ -547,11 +402,11 @@ if __name__ == "__main__":
547
402
shutil.copytree(args.scripts, tmp_scripts_path)
548
403
if args.postinstall:
549
404
if not os.path.exists(args.postinstall):
550
- print "postinstall file %s does not exist!" % args .postinstall
405
+ print( "postinstall file %s does not exist!" % args.postinstall)
551
406
cleanup_and_exit(1)
552
407
postinstall_path = os.path.join(tmp_scripts_path, "postinstall")
553
408
if os.path.exists(postinstall_path):
554
- print "postinstall script already exists in %s" % args .scripts
409
+ print( "postinstall script already exists in %s" % args.scripts)
555
410
cleanup_and_exit(1)
556
411
logger("copying %s to %s" % (args.postinstall, postinstall_path), 1)
557
412
shutil.copy2(args.postinstall, postinstall_path)
@@ -560,11 +415,11 @@ if __name__ == "__main__":
560
415
stat.S_IROTH | stat.S_IXOTH)
561
416
if args.preinstall:
562
417
if not os.path.exists(args.preinstall):
563
- print "preinstall file %s does not exist!" % args .preinstall
418
+ print( "preinstall file %s does not exist!" % args.preinstall)
564
419
cleanup_and_exit(1)
565
420
preinstall_path = os.path.join(tmp_scripts_path, "preinstall")
566
421
if os.path.exists(preinstall_path):
567
- print "preinstall script already exists in %s" % args .scripts
422
+ print( "preinstall script already exists in %s" % args.scripts)
568
423
cleanup_and_exit(1)
569
424
logger("copying %s to %s" % (args.preinstall, preinstall_path), 1)
570
425
shutil.copy2(args.preinstall, preinstall_path)
@@ -593,9 +448,9 @@ if __name__ == "__main__":
593
448
594
449
logger(result["stdout"], 1)
595
450
if result["return_code"] != 0:
596
- print "Error Code: %d " % result ["return_code" ]
597
- print result ["stderr" ]
451
+ print( "Error Code: %d " % result["return_code"])
452
+ print( result["stderr"])
598
453
else:
599
- print pkg_path
454
+ print( pkg_path)
600
455
601
456
cleanup_and_exit(0)
0 commit comments