1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/webrtc/trunk/build/mac/tweak_info_plist.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,293 @@ 1.4 +#!/usr/bin/env python 1.5 + 1.6 +# Copyright (c) 2012 The Chromium Authors. All rights reserved. 1.7 +# Use of this source code is governed by a BSD-style license that can be 1.8 +# found in the LICENSE file. 1.9 + 1.10 +# 1.11 +# Xcode supports build variable substitutions and CPP; sadly, that doesn't work 1.12 +# because: 1.13 +# 1.14 +# 1. Xcode wants to do the Info.plist work before it runs any build phases, 1.15 +# this means if we were to generate a .h file for INFOPLIST_PREFIX_HEADER 1.16 +# we'd have to put it in another target so it runs in time. 1.17 +# 2. Xcode also doesn't check to see if the header being used as a prefix for 1.18 +# the Info.plist has changed. So even if we updated it, it's only looking 1.19 +# at the modtime of the info.plist to see if that's changed. 1.20 +# 1.21 +# So, we work around all of this by making a script build phase that will run 1.22 +# during the app build, and simply update the info.plist in place. This way 1.23 +# by the time the app target is done, the info.plist is correct. 1.24 +# 1.25 + 1.26 +import optparse 1.27 +import os 1.28 +from os import environ as env 1.29 +import plistlib 1.30 +import re 1.31 +import subprocess 1.32 +import sys 1.33 +import tempfile 1.34 + 1.35 +TOP = os.path.join(env['SRCROOT'], '..') 1.36 + 1.37 +sys.path.insert(0, os.path.join(TOP, "build/util")) 1.38 +import lastchange 1.39 + 1.40 + 1.41 +def _GetOutput(args): 1.42 + """Runs a subprocess and waits for termination. Returns (stdout, returncode) 1.43 + of the process. stderr is attached to the parent.""" 1.44 + proc = subprocess.Popen(args, stdout=subprocess.PIPE) 1.45 + (stdout, stderr) = proc.communicate() 1.46 + return (stdout, proc.returncode) 1.47 + 1.48 + 1.49 +def _GetOutputNoError(args): 1.50 + """Similar to _GetOutput() but ignores stderr. If there's an error launching 1.51 + the child (like file not found), the exception will be caught and (None, 1) 1.52 + will be returned to mimic quiet failure.""" 1.53 + try: 1.54 + proc = subprocess.Popen(args, stdout=subprocess.PIPE, 1.55 + stderr=subprocess.PIPE) 1.56 + except OSError: 1.57 + return (None, 1) 1.58 + (stdout, stderr) = proc.communicate() 1.59 + return (stdout, proc.returncode) 1.60 + 1.61 + 1.62 +def _RemoveKeys(plist, *keys): 1.63 + """Removes a varargs of keys from the plist.""" 1.64 + for key in keys: 1.65 + try: 1.66 + del plist[key] 1.67 + except KeyError: 1.68 + pass 1.69 + 1.70 + 1.71 +def _AddVersionKeys(plist): 1.72 + """Adds the product version number into the plist. Returns True on success and 1.73 + False on error. The error will be printed to stderr.""" 1.74 + # Pull in the Chrome version number. 1.75 + VERSION_TOOL = os.path.join(TOP, 'chrome/tools/build/version.py') 1.76 + VERSION_FILE = os.path.join(TOP, 'chrome/VERSION') 1.77 + 1.78 + (stdout, retval1) = _GetOutput([VERSION_TOOL, '-f', VERSION_FILE, '-t', 1.79 + '@MAJOR@.@MINOR@.@BUILD@.@PATCH@']) 1.80 + full_version = stdout.rstrip() 1.81 + 1.82 + (stdout, retval2) = _GetOutput([VERSION_TOOL, '-f', VERSION_FILE, '-t', 1.83 + '@BUILD@.@PATCH@']) 1.84 + bundle_version = stdout.rstrip() 1.85 + 1.86 + # If either of the two version commands finished with non-zero returncode, 1.87 + # report the error up. 1.88 + if retval1 or retval2: 1.89 + return False 1.90 + 1.91 + # Add public version info so "Get Info" works. 1.92 + plist['CFBundleShortVersionString'] = full_version 1.93 + 1.94 + # Honor the 429496.72.95 limit. The maximum comes from splitting 2^32 - 1 1.95 + # into 6, 2, 2 digits. The limitation was present in Tiger, but it could 1.96 + # have been fixed in later OS release, but hasn't been tested (it's easy 1.97 + # enough to find out with "lsregister -dump). 1.98 + # http://lists.apple.com/archives/carbon-dev/2006/Jun/msg00139.html 1.99 + # BUILD will always be an increasing value, so BUILD_PATH gives us something 1.100 + # unique that meetings what LS wants. 1.101 + plist['CFBundleVersion'] = bundle_version 1.102 + 1.103 + # Return with no error. 1.104 + return True 1.105 + 1.106 + 1.107 +def _DoSCMKeys(plist, add_keys): 1.108 + """Adds the SCM information, visible in about:version, to property list. If 1.109 + |add_keys| is True, it will insert the keys, otherwise it will remove them.""" 1.110 + scm_path, scm_revision = None, None 1.111 + if add_keys: 1.112 + version_info = lastchange.FetchVersionInfo( 1.113 + default_lastchange=None, directory=TOP) 1.114 + scm_path, scm_revision = version_info.url, version_info.revision 1.115 + 1.116 + # See if the operation failed. 1.117 + _RemoveKeys(plist, 'SCMRevision') 1.118 + if scm_revision != None: 1.119 + plist['SCMRevision'] = scm_revision 1.120 + elif add_keys: 1.121 + print >>sys.stderr, 'Could not determine SCM revision. This may be OK.' 1.122 + 1.123 + if scm_path != None: 1.124 + plist['SCMPath'] = scm_path 1.125 + else: 1.126 + _RemoveKeys(plist, 'SCMPath') 1.127 + 1.128 + 1.129 +def _DoPDFKeys(plist, add_keys): 1.130 + """Adds PDF support to the document types list. If add_keys is True, it will 1.131 + add the type information dictionary. If it is False, it will remove it if 1.132 + present.""" 1.133 + 1.134 + PDF_FILE_EXTENSION = 'pdf' 1.135 + 1.136 + def __AddPDFKeys(sub_plist): 1.137 + """Writes the keys into a sub-dictionary of the plist.""" 1.138 + sub_plist['CFBundleTypeExtensions'] = [PDF_FILE_EXTENSION] 1.139 + sub_plist['CFBundleTypeIconFile'] = 'document.icns' 1.140 + sub_plist['CFBundleTypeMIMETypes'] = 'application/pdf' 1.141 + sub_plist['CFBundleTypeName'] = 'PDF Document' 1.142 + sub_plist['CFBundleTypeRole'] = 'Viewer' 1.143 + 1.144 + DOCUMENT_TYPES_KEY = 'CFBundleDocumentTypes' 1.145 + 1.146 + # First get the list of document types, creating it if necessary. 1.147 + try: 1.148 + extensions = plist[DOCUMENT_TYPES_KEY] 1.149 + except KeyError: 1.150 + # If this plist doesn't have a type dictionary, create one if set to add the 1.151 + # keys. If not, bail. 1.152 + if not add_keys: 1.153 + return 1.154 + extensions = plist[DOCUMENT_TYPES_KEY] = [] 1.155 + 1.156 + # Loop over each entry in the list, looking for one that handles PDF types. 1.157 + for i, ext in enumerate(extensions): 1.158 + # If an entry for .pdf files is found... 1.159 + if 'CFBundleTypeExtensions' not in ext: 1.160 + continue 1.161 + if PDF_FILE_EXTENSION in ext['CFBundleTypeExtensions']: 1.162 + if add_keys: 1.163 + # Overwrite the existing keys with new ones. 1.164 + __AddPDFKeys(ext) 1.165 + else: 1.166 + # Otherwise, delete the entry entirely. 1.167 + del extensions[i] 1.168 + return 1.169 + 1.170 + # No PDF entry exists. If one needs to be added, do so now. 1.171 + if add_keys: 1.172 + pdf_entry = {} 1.173 + __AddPDFKeys(pdf_entry) 1.174 + extensions.append(pdf_entry) 1.175 + 1.176 + 1.177 +def _AddBreakpadKeys(plist, branding): 1.178 + """Adds the Breakpad keys. This must be called AFTER _AddVersionKeys() and 1.179 + also requires the |branding| argument.""" 1.180 + plist['BreakpadReportInterval'] = '3600' # Deliberately a string. 1.181 + plist['BreakpadProduct'] = '%s_Mac' % branding 1.182 + plist['BreakpadProductDisplay'] = branding 1.183 + plist['BreakpadVersion'] = plist['CFBundleShortVersionString'] 1.184 + # These are both deliberately strings and not boolean. 1.185 + plist['BreakpadSendAndExit'] = 'YES' 1.186 + plist['BreakpadSkipConfirm'] = 'YES' 1.187 + 1.188 + 1.189 +def _RemoveBreakpadKeys(plist): 1.190 + """Removes any set Breakpad keys.""" 1.191 + _RemoveKeys(plist, 1.192 + 'BreakpadURL', 1.193 + 'BreakpadReportInterval', 1.194 + 'BreakpadProduct', 1.195 + 'BreakpadProductDisplay', 1.196 + 'BreakpadVersion', 1.197 + 'BreakpadSendAndExit', 1.198 + 'BreakpadSkipConfirm') 1.199 + 1.200 + 1.201 +def _AddKeystoneKeys(plist, bundle_identifier): 1.202 + """Adds the Keystone keys. This must be called AFTER _AddVersionKeys() and 1.203 + also requires the |bundle_identifier| argument (com.example.product).""" 1.204 + plist['KSVersion'] = plist['CFBundleShortVersionString'] 1.205 + plist['KSProductID'] = bundle_identifier 1.206 + plist['KSUpdateURL'] = 'https://tools.google.com/service/update2' 1.207 + 1.208 + 1.209 +def _RemoveKeystoneKeys(plist): 1.210 + """Removes any set Keystone keys.""" 1.211 + _RemoveKeys(plist, 1.212 + 'KSVersion', 1.213 + 'KSProductID', 1.214 + 'KSUpdateURL') 1.215 + 1.216 + 1.217 +def Main(argv): 1.218 + parser = optparse.OptionParser('%prog [options]') 1.219 + parser.add_option('--breakpad', dest='use_breakpad', action='store', 1.220 + type='int', default=False, help='Enable Breakpad [1 or 0]') 1.221 + parser.add_option('--breakpad_uploads', dest='breakpad_uploads', 1.222 + action='store', type='int', default=False, 1.223 + help='Enable Breakpad\'s uploading of crash dumps [1 or 0]') 1.224 + parser.add_option('--keystone', dest='use_keystone', action='store', 1.225 + type='int', default=False, help='Enable Keystone [1 or 0]') 1.226 + parser.add_option('--scm', dest='add_scm_info', action='store', type='int', 1.227 + default=True, help='Add SCM metadata [1 or 0]') 1.228 + parser.add_option('--pdf', dest='add_pdf_support', action='store', type='int', 1.229 + default=False, help='Add PDF file handler support [1 or 0]') 1.230 + parser.add_option('--branding', dest='branding', action='store', 1.231 + type='string', default=None, help='The branding of the binary') 1.232 + parser.add_option('--bundle_id', dest='bundle_identifier', 1.233 + action='store', type='string', default=None, 1.234 + help='The bundle id of the binary') 1.235 + (options, args) = parser.parse_args(argv) 1.236 + 1.237 + if len(args) > 0: 1.238 + print >>sys.stderr, parser.get_usage() 1.239 + return 1 1.240 + 1.241 + # Read the plist into its parsed format. 1.242 + DEST_INFO_PLIST = os.path.join(env['TARGET_BUILD_DIR'], env['INFOPLIST_PATH']) 1.243 + plist = plistlib.readPlist(DEST_INFO_PLIST) 1.244 + 1.245 + # Insert the product version. 1.246 + if not _AddVersionKeys(plist): 1.247 + return 2 1.248 + 1.249 + # Add Breakpad if configured to do so. 1.250 + if options.use_breakpad: 1.251 + if options.branding is None: 1.252 + print >>sys.stderr, 'Use of Breakpad requires branding.' 1.253 + return 1 1.254 + _AddBreakpadKeys(plist, options.branding) 1.255 + if options.breakpad_uploads: 1.256 + plist['BreakpadURL'] = 'https://clients2.google.com/cr/report' 1.257 + else: 1.258 + # This allows crash dumping to a file without uploading the 1.259 + # dump, for testing purposes. Breakpad does not recognise 1.260 + # "none" as a special value, but this does stop crash dump 1.261 + # uploading from happening. We need to specify something 1.262 + # because if "BreakpadURL" is not present, Breakpad will not 1.263 + # register its crash handler and no crash dumping will occur. 1.264 + plist['BreakpadURL'] = 'none' 1.265 + else: 1.266 + _RemoveBreakpadKeys(plist) 1.267 + 1.268 + # Only add Keystone in Release builds. 1.269 + if options.use_keystone and env['CONFIGURATION'] == 'Release': 1.270 + if options.bundle_identifier is None: 1.271 + print >>sys.stderr, 'Use of Keystone requires the bundle id.' 1.272 + return 1 1.273 + _AddKeystoneKeys(plist, options.bundle_identifier) 1.274 + else: 1.275 + _RemoveKeystoneKeys(plist) 1.276 + 1.277 + # Adds or removes any SCM keys. 1.278 + _DoSCMKeys(plist, options.add_scm_info) 1.279 + 1.280 + # Adds or removes the PDF file handler entry. 1.281 + _DoPDFKeys(plist, options.add_pdf_support) 1.282 + 1.283 + # Now that all keys have been mutated, rewrite the file. 1.284 + temp_info_plist = tempfile.NamedTemporaryFile() 1.285 + plistlib.writePlist(plist, temp_info_plist.name) 1.286 + 1.287 + # Info.plist will work perfectly well in any plist format, but traditionally 1.288 + # applications use xml1 for this, so convert it to ensure that it's valid. 1.289 + proc = subprocess.Popen(['plutil', '-convert', 'xml1', '-o', DEST_INFO_PLIST, 1.290 + temp_info_plist.name]) 1.291 + proc.wait() 1.292 + return proc.returncode 1.293 + 1.294 + 1.295 +if __name__ == '__main__': 1.296 + sys.exit(Main(sys.argv[1:]))