1#!/usr/bin/env python 2 3"""PList utility""" 4 5import argparse 6import os 7import subprocess 8import re 9import unicodedata 10 11def normalize_char(c): 12 try: 13 cname = unicodedata.name( unicode(c) ) 14 cname = cname[:cname.index( ' WITH' )] 15 return unicodedata.lookup( cname ) 16 except ( ValueError, KeyError ): 17 return c 18 19def normalize_string(s): 20 return ''.join( normalize_char(c) for c in s ) 21 22def replace_var( str, var, val ): 23 if str.find( '$(' + var + ')' ) != -1: 24 return str.replace( '$(' + var + ')', val ) 25 if str.find( '${' + var + '}' ) != -1: 26 return str.replace( '${' + var + '}', val ) 27 return str 28 29 30parser = argparse.ArgumentParser( description = 'PList utility for Ninja builds' ) 31parser.add_argument( 'files', 32 metavar = 'file', type=file, nargs='+', 33 help = 'Source plist file' ) 34parser.add_argument( '--exename', type=str, 35 help = 'Executable name', 36 default = [] ) 37parser.add_argument( '--prodname', type=str, 38 help = 'Product name', 39 default = [] ) 40parser.add_argument( '--bundle', type=str, 41 help = 'Bundle identifier', 42 default = [] ) 43parser.add_argument( '--output', type=str, 44 help = 'Output path', 45 default = [] ) 46parser.add_argument( '--target', type=str, 47 help = 'Target OS', 48 default = [] ) 49parser.add_argument( '--deploymenttarget', type=str, 50 help = 'Target OS version', 51 default = [] ) 52options = parser.parse_args() 53 54if not options.exename: 55 options.exename = 'unknown' 56if not options.prodname: 57 options.prodname = 'unknown' 58if not options.target: 59 options.target = 'macos' 60if not options.deploymenttarget: 61 if options.target == 'macos': 62 options.deploymenttarget = '10.7' 63 else: 64 options.deploymenttarget = '6.0' 65 66buildversion = subprocess.check_output( [ 'sw_vers', '-buildVersion' ] ).strip() 67 68#Merge inputs using first file as base 69lines = [] 70for f in options.files: 71 if lines == []: 72 lines += [ line.strip( '\n\r' ) for line in f ] 73 else: 74 mergelines = [ line.strip( '\n\r' ) for line in f ] 75 for i in range( 0, len( mergelines ) ): 76 if re.match( '^<dict/>$', mergelines[i] ): 77 break 78 if re.match( '^<dict>$', mergelines[i] ): 79 for j in range( 0, len( lines ) ): 80 if re.match( '^</dict>$', lines[j] ): 81 for k in range( i+1, len( mergelines ) ): 82 if re.match( '^</dict>$', mergelines[k] ): 83 break 84 lines.insert( j+(k-(i+1)), mergelines[k] ) 85 break 86 break 87 88#Parse input plist to get package type and signature 89bundle_package_type = 'APPL' 90bundle_signature = '????' 91 92for i in range( 0, len( lines ) ): 93 if 'CFBundlePackageType' in lines[i]: 94 match = re.match( '^.*>(.*)<.*$', lines[i+1] ) 95 if match: 96 bundle_package_type = match.group(1) 97 if 'CFBundleSignature' in lines[i]: 98 match = re.match( '^.*>(.*)<.*$', lines[i+1] ) 99 if match: 100 bundle_signature = match.group(1) 101 102#Write package type and signature to PkgInfo in output path 103with open( os.path.join( os.path.dirname( options.output ), 'PkgInfo' ), 'w' ) as pkginfo_file: 104 pkginfo_file.write( bundle_package_type + bundle_signature ) 105 pkginfo_file.close() 106 107#insert os version 108for i in range( 0, len( lines ) ): 109 if re.match( '^<dict>$', lines[i] ): 110 lines.insert( i+1, '\t<key>BuildMachineOSBuild</key>' ) 111 lines.insert( i+2, '\t<string>' + buildversion + '</string>' ) 112 break 113 114#replace build variables name 115for i in range( 0, len( lines ) ): 116 lines[i] = replace_var( lines[i], 'EXECUTABLE_NAME', options.exename ) 117 lines[i] = replace_var( lines[i], 'PRODUCT_NAME', options.prodname ) 118 lines[i] = replace_var( lines[i], 'PRODUCT_NAME:rfc1034identifier', normalize_string( options.exename ).lower() ) 119 lines[i] = replace_var( lines[i], 'PRODUCT_NAME:c99extidentifier', normalize_string( options.exename ).lower().replace( '-', '_' ).replace( '.', '_' ) ) 120 lines[i] = replace_var( lines[i], 'IOS_DEPLOYMENT_TARGET', options.deploymenttarget ) 121 lines[i] = replace_var( lines[i], 'MACOSX_DEPLOYMENT_TARGET', options.deploymenttarget ) 122 123#replace bundle identifier if given 124if not options.bundle is None and options.bundle != '': 125 for i in range( 0, len( lines ) ): 126 if lines[i].find( 'CFBundleIdentifier' ) != -1: 127 lines[i+1] = '\t<string>' + normalize_string( options.bundle ) + '</string>' 128 break 129 130#add supported platform, minimum os version and requirements 131if options.target == 'ios': 132 for i in range( 0, len( lines ) ): 133 if 'CFBundleSignature' in lines[i]: 134 lines.insert( i+2, '\t<key>CFBundleSupportedPlatforms</key>' ) 135 lines.insert( i+3, '\t<array>' ) 136 lines.insert( i+4, '\t\t<string>iPhoneOS</string>' ) 137 lines.insert( i+5, '\t</array>' ) 138 lines.insert( i+6, '\t<key>MinimumOSVersion</key>' ) 139 lines.insert( i+7, '\t<string>6.0</string>' ) 140 lines.insert( i+8, '\t<key>UIDeviceFamily</key>' ) 141 lines.insert( i+9, '\t<array>' ) 142 lines.insert( i+10, '\t\t<integer>1</integer>' ) 143 lines.insert( i+11, '\t\t<integer>2</integer>' ) 144 lines.insert( i+12, '\t</array>' ) 145 break 146 147#add build info 148#<key>DTCompiler</key> 149#<string>com.apple.compilers.llvm.clang.1_0</string> 150#<key>DTPlatformBuild</key> 151#<string>12B411</string> 152#<key>DTPlatformName</key> 153#<string>iphoneos</string> 154#<key>DTPlatformVersion</key> 155#<string>8.1</string> 156#<key>DTSDKBuild</key> 157#<string>12B411</string> 158#<key>DTSDKName</key> 159#<string>iphoneos8.1</string> 160#<key>DTXcode</key> 161#<string>0611</string> 162#<key>DTXcodeBuild</key> 163#<string>6A2008a</string> 164 165#write final Info.plist in output path 166with open( options.output, 'w' ) as plist_file: 167 for line in lines: 168 #print lines[i] 169 plist_file.write( line + '\n' ) 170 plist_file.close() 171 172#run plutil -convert binary1 173sdk = 'iphoneos' 174platformpath = subprocess.check_output( [ 'xcrun', '--sdk', sdk, '--show-sdk-platform-path' ] ).strip() 175localpath = platformpath + "/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin" 176plutil = "PATH=" + localpath + " " + subprocess.check_output( [ 'xcrun', '--sdk', sdk, '-f', 'plutil' ] ).strip() 177os.system( plutil + ' -convert binary1 ' + options.output ) 178 179