@@ -4,16 +4,161 @@ import argparse
4
4
import string
5
5
import os
6
6
import subprocess
7
- import plistlib
8
7
import tempfile
9
8
import shutil
10
9
import stat
11
10
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 )
12
147
13
- quickpkg_version = '0.3'
14
- supported_extensions = ['dmg' , 'app' , 'zip' ]
15
148
149
+ def writePlistToString (plistObject ):
150
+ '''Create a plist-formatted string from plistObject.'''
151
+ return str (_plistToData (plistObject ))
152
+
153
+
154
+ #
16
155
# quickpkg
156
+ #
157
+
158
+
159
+ quickpkg_version = '0.4'
160
+ supported_extensions = ['dmg' , 'app' , 'zip' ]
161
+
17
162
18
163
# modeled after munkiimport but to build a pkg
19
164
@@ -69,7 +214,7 @@ def dmg_has_sla(dmgpath):
69
214
print "error getting imageinfo! %s, %s" % (result ["return_code" ], result ["stderr" ])
70
215
return False
71
216
result_plist = result ["stdout" ]
72
- imageinfo_dict = plistlib . readPlistFromString (result_plist )
217
+ imageinfo_dict = readPlistFromString (result_plist )
73
218
properties = imageinfo_dict .get ('Properties' )
74
219
if properties is not None :
75
220
has_sla = properties .get ('Software License Agreement' , False )
@@ -83,7 +228,7 @@ def attachdmg(dmgpath):
83
228
if info_result ["return_code" ] == 0 :
84
229
# parse the plist output
85
230
(theplist , alltext ) = getFirstPlist (info_result ["stdout" ])
86
- info_dict = plistlib . readPlistFromString (theplist )
231
+ info_dict = readPlistFromString (theplist )
87
232
volpaths = []
88
233
if "images" in info_dict .keys ():
89
234
for y in info_dict ["images" ]:
@@ -115,7 +260,7 @@ def attachdmg(dmgpath):
115
260
if result ["return_code" ] == 0 :
116
261
# parse the plist output
117
262
(theplist , alltext ) = getFirstPlist (result ["stdout" ])
118
- resultdict = plistlib . readPlistFromString (theplist )
263
+ resultdict = readPlistFromString (theplist )
119
264
volpaths = []
120
265
for x in resultdict ["system-entities" ]:
121
266
if x ["potentially-mountable" ]:
@@ -157,7 +302,7 @@ def appNameAndVersion(app_path):
157
302
print "Application at path %s does not have Info.plist" % app_path
158
303
# TODO: cleanup volumes here
159
304
cleanup_and_exit (1 )
160
- info_plist = plistlib . readPlist (info_path )
305
+ info_plist = readPlist (info_path )
161
306
app_name = info_plist .get ("CFBundleName" )
162
307
if app_name is None :
163
308
app_name = info_plist .get ("CFBundleDisplayName" )
0 commit comments