@@ -4,16 +4,161 @@ import argparse
44import string
55import os
66import subprocess
7- import plistlib
87import tempfile
98import shutil
109import stat
1110
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 )
12147
13- quickpkg_version = '0.3'
14- supported_extensions = ['dmg' , 'app' , 'zip' ]
15148
149+ def writePlistToString (plistObject ):
150+ '''Create a plist-formatted string from plistObject.'''
151+ return str (_plistToData (plistObject ))
152+
153+
154+ #
16155# quickpkg
156+ #
157+
158+
159+ quickpkg_version = '0.4'
160+ supported_extensions = ['dmg' , 'app' , 'zip' ]
161+
17162
18163# modeled after munkiimport but to build a pkg
19164
@@ -69,7 +214,7 @@ def dmg_has_sla(dmgpath):
69214 print "error getting imageinfo! %s, %s" % (result ["return_code" ], result ["stderr" ])
70215 return False
71216 result_plist = result ["stdout" ]
72- imageinfo_dict = plistlib . readPlistFromString (result_plist )
217+ imageinfo_dict = readPlistFromString (result_plist )
73218 properties = imageinfo_dict .get ('Properties' )
74219 if properties is not None :
75220 has_sla = properties .get ('Software License Agreement' , False )
@@ -83,7 +228,7 @@ def attachdmg(dmgpath):
83228 if info_result ["return_code" ] == 0 :
84229 # parse the plist output
85230 (theplist , alltext ) = getFirstPlist (info_result ["stdout" ])
86- info_dict = plistlib . readPlistFromString (theplist )
231+ info_dict = readPlistFromString (theplist )
87232 volpaths = []
88233 if "images" in info_dict .keys ():
89234 for y in info_dict ["images" ]:
@@ -115,7 +260,7 @@ def attachdmg(dmgpath):
115260 if result ["return_code" ] == 0 :
116261 # parse the plist output
117262 (theplist , alltext ) = getFirstPlist (result ["stdout" ])
118- resultdict = plistlib . readPlistFromString (theplist )
263+ resultdict = readPlistFromString (theplist )
119264 volpaths = []
120265 for x in resultdict ["system-entities" ]:
121266 if x ["potentially-mountable" ]:
@@ -157,7 +302,7 @@ def appNameAndVersion(app_path):
157302 print "Application at path %s does not have Info.plist" % app_path
158303 # TODO: cleanup volumes here
159304 cleanup_and_exit (1 )
160- info_plist = plistlib . readPlist (info_path )
305+ info_plist = readPlist (info_path )
161306 app_name = info_plist .get ("CFBundleName" )
162307 if app_name is None :
163308 app_name = info_plist .get ("CFBundleDisplayName" )
0 commit comments