Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 | 2 |
| 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 # | 7 # |
| 8 # Xcode supports build variable substitutions and CPP; sadly, that doesn't work | 8 # Xcode supports build variable substitutions and CPP; sadly, that doesn't work |
| 9 # because: | 9 # because: |
| 10 # | 10 # |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 24 import os | 24 import os |
| 25 import plistlib | 25 import plistlib |
| 26 import re | 26 import re |
| 27 import subprocess | 27 import subprocess |
| 28 import sys | 28 import sys |
| 29 import tempfile | 29 import tempfile |
| 30 | 30 |
| 31 TOP = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) | 31 TOP = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) |
| 32 | 32 |
| 33 | 33 |
| 34 def _ConvertPlist(source_plist, output_plist, fmt): | |
| 35 """Convert |source_plist| to |fmt| and save as |output_plist|.""" | |
| 36 return subprocess.call( | |
| 37 ['plutil', '-convert', fmt, '-o', output_plist, source_plist]) | |
| 38 | |
| 39 | |
| 34 def _GetOutput(args): | 40 def _GetOutput(args): |
| 35 """Runs a subprocess and waits for termination. Returns (stdout, returncode) | 41 """Runs a subprocess and waits for termination. Returns (stdout, returncode) |
| 36 of the process. stderr is attached to the parent.""" | 42 of the process. stderr is attached to the parent.""" |
| 37 proc = subprocess.Popen(args, stdout=subprocess.PIPE) | 43 proc = subprocess.Popen(args, stdout=subprocess.PIPE) |
| 38 (stdout, stderr) = proc.communicate() | 44 (stdout, stderr) = proc.communicate() |
| 39 return (stdout, proc.returncode) | 45 return (stdout, proc.returncode) |
| 40 | 46 |
| 41 | 47 |
| 42 def _GetOutputNoError(args): | 48 def _GetOutputNoError(args): |
| 43 """Similar to _GetOutput() but ignores stderr. If there's an error launching | 49 """Similar to _GetOutput() but ignores stderr. If there's an error launching |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 54 | 60 |
| 55 def _RemoveKeys(plist, *keys): | 61 def _RemoveKeys(plist, *keys): |
| 56 """Removes a varargs of keys from the plist.""" | 62 """Removes a varargs of keys from the plist.""" |
| 57 for key in keys: | 63 for key in keys: |
| 58 try: | 64 try: |
| 59 del plist[key] | 65 del plist[key] |
| 60 except KeyError: | 66 except KeyError: |
| 61 pass | 67 pass |
| 62 | 68 |
| 63 | 69 |
| 64 def _AddVersionKeys(plist, version=None): | 70 def _ApplyVersionOverrides(version, keys, overrides, separator='.'): |
| 71 """Applies version overrides. | |
| 72 | |
| 73 Given a |version| string as "a.b.c.d" (assuming a default separator) with | |
| 74 version components named by |keys| then overrides any value that is present | |
| 75 in |overrides|. | |
| 76 | |
| 77 >>> _ApplyVersionOverrides('a.b', ['major', 'minor'], {'minor': 'd'}) | |
| 78 'a.d' | |
| 79 """ | |
| 80 if not overrides: | |
| 81 return version | |
| 82 version_values = version.split(separator) | |
| 83 for i, (key, value) in enumerate(zip(keys, version_values)): | |
| 84 if key in overrides: | |
| 85 version_values[i] = overrides[key] | |
| 86 return separator.join(version_values) | |
| 87 | |
| 88 | |
| 89 def _AddVersionKeys(plist, version=None, overrides=None): | |
| 65 """Adds the product version number into the plist. Returns True on success and | 90 """Adds the product version number into the plist. Returns True on success and |
| 66 False on error. The error will be printed to stderr.""" | 91 False on error. The error will be printed to stderr.""" |
| 67 if version: | 92 if version: |
| 68 match = re.match('\d+\.\d+\.(\d+\.\d+)$', version) | 93 match = re.match('\d+\.\d+\.(\d+\.\d+)$', version) |
| 69 if not match: | 94 if not match: |
| 70 print >>sys.stderr, 'Invalid version string specified: "%s"' % version | 95 print >>sys.stderr, 'Invalid version string specified: "%s"' % version |
| 71 return False | 96 return False |
| 72 | 97 |
| 73 full_version = match.group(0) | 98 full_version = match.group(0) |
| 74 bundle_version = match.group(1) | 99 bundle_version = match.group(1) |
| 75 | 100 |
| 76 else: | 101 else: |
| 77 # Pull in the Chrome version number. | 102 # Pull in the Chrome version number. |
| 78 VERSION_TOOL = os.path.join(TOP, 'build/util/version.py') | 103 VERSION_TOOL = os.path.join(TOP, 'build/util/version.py') |
| 79 VERSION_FILE = os.path.join(TOP, 'chrome/VERSION') | 104 VERSION_FILE = os.path.join(TOP, 'chrome/VERSION') |
| 80 | 105 |
| 81 (stdout, retval1) = _GetOutput([VERSION_TOOL, '-f', VERSION_FILE, '-t', | 106 (stdout, retval1) = _GetOutput([VERSION_TOOL, '-f', VERSION_FILE, '-t', |
| 82 '@MAJOR@.@MINOR@.@BUILD@.@PATCH@']) | 107 '@MAJOR@.@MINOR@.@BUILD@.@PATCH@']) |
| 83 full_version = stdout.rstrip() | 108 full_version = _ApplyVersionOverrides( |
| 109 stdout.rstrip(), ('MAJOR', 'MINOR', 'BUILD', 'PATCH'), overrides) | |
| 84 | 110 |
| 85 (stdout, retval2) = _GetOutput([VERSION_TOOL, '-f', VERSION_FILE, '-t', | 111 (stdout, retval2) = _GetOutput([VERSION_TOOL, '-f', VERSION_FILE, '-t', |
| 86 '@BUILD@.@PATCH@']) | 112 '@BUILD@.@PATCH@']) |
| 87 bundle_version = stdout.rstrip() | 113 bundle_version = _ApplyVersionOverrides( |
| 114 stdout.rstrip(), ('BUILD', 'PATCH'), overrides) | |
| 88 | 115 |
| 89 # If either of the two version commands finished with non-zero returncode, | 116 # If either of the two version commands finished with non-zero returncode, |
| 90 # report the error up. | 117 # report the error up. |
| 91 if retval1 or retval2: | 118 if retval1 or retval2: |
| 92 return False | 119 return False |
| 93 | 120 |
| 94 # Add public version info so "Get Info" works. | 121 # Add public version info so "Get Info" works. |
| 95 plist['CFBundleShortVersionString'] = full_version | 122 plist['CFBundleShortVersionString'] = full_version |
| 96 | 123 |
| 97 # Honor the 429496.72.95 limit. The maximum comes from splitting 2^32 - 1 | 124 # Honor the 429496.72.95 limit. The maximum comes from splitting 2^32 - 1 |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 124 # See if the operation failed. | 151 # See if the operation failed. |
| 125 _RemoveKeys(plist, 'SCMRevision') | 152 _RemoveKeys(plist, 'SCMRevision') |
| 126 if scm_revision != None: | 153 if scm_revision != None: |
| 127 plist['SCMRevision'] = scm_revision | 154 plist['SCMRevision'] = scm_revision |
| 128 elif add_keys: | 155 elif add_keys: |
| 129 print >>sys.stderr, 'Could not determine SCM revision. This may be OK.' | 156 print >>sys.stderr, 'Could not determine SCM revision. This may be OK.' |
| 130 | 157 |
| 131 return True | 158 return True |
| 132 | 159 |
| 133 | 160 |
| 134 def _AddBreakpadKeys(plist, branding): | 161 def _AddBreakpadKeys(plist, branding, platform): |
| 135 """Adds the Breakpad keys. This must be called AFTER _AddVersionKeys() and | 162 """Adds the Breakpad keys. This must be called AFTER _AddVersionKeys() and |
| 136 also requires the |branding| argument.""" | 163 also requires the |branding| argument.""" |
| 137 plist['BreakpadReportInterval'] = '3600' # Deliberately a string. | 164 plist['BreakpadReportInterval'] = '3600' # Deliberately a string. |
| 138 plist['BreakpadProduct'] = '%s_Mac' % branding | 165 plist['BreakpadProduct'] = '%s_%s' % ( |
| 166 branding, {'mac': 'Mac', 'ios': 'iOS'}[platform]) | |
| 139 plist['BreakpadProductDisplay'] = branding | 167 plist['BreakpadProductDisplay'] = branding |
| 140 plist['BreakpadVersion'] = plist['CFBundleShortVersionString'] | 168 plist['BreakpadVersion'] = plist['CFBundleShortVersionString'] |
| 141 # These are both deliberately strings and not boolean. | 169 # These are both deliberately strings and not boolean. |
| 142 plist['BreakpadSendAndExit'] = 'YES' | 170 plist['BreakpadSendAndExit'] = 'YES' |
| 143 plist['BreakpadSkipConfirm'] = 'YES' | 171 plist['BreakpadSkipConfirm'] = 'YES' |
| 144 | 172 |
| 145 | 173 |
| 146 def _RemoveBreakpadKeys(plist): | 174 def _RemoveBreakpadKeys(plist): |
| 147 """Removes any set Breakpad keys.""" | 175 """Removes any set Breakpad keys.""" |
| 148 _RemoveKeys(plist, | 176 _RemoveKeys(plist, |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 213 help='Enable Breakpad\'s uploading of crash dumps [1 or 0]') | 241 help='Enable Breakpad\'s uploading of crash dumps [1 or 0]') |
| 214 parser.add_option('--keystone', dest='use_keystone', action='store', | 242 parser.add_option('--keystone', dest='use_keystone', action='store', |
| 215 type='int', default=False, help='Enable Keystone [1 or 0]') | 243 type='int', default=False, help='Enable Keystone [1 or 0]') |
| 216 parser.add_option('--scm', dest='add_scm_info', action='store', type='int', | 244 parser.add_option('--scm', dest='add_scm_info', action='store', type='int', |
| 217 default=True, help='Add SCM metadata [1 or 0]') | 245 default=True, help='Add SCM metadata [1 or 0]') |
| 218 parser.add_option('--branding', dest='branding', action='store', | 246 parser.add_option('--branding', dest='branding', action='store', |
| 219 type='string', default=None, help='The branding of the binary') | 247 type='string', default=None, help='The branding of the binary') |
| 220 parser.add_option('--bundle_id', dest='bundle_identifier', | 248 parser.add_option('--bundle_id', dest='bundle_identifier', |
| 221 action='store', type='string', default=None, | 249 action='store', type='string', default=None, |
| 222 help='The bundle id of the binary') | 250 help='The bundle id of the binary') |
| 251 parser.add_option('--platform', choices=('ios', 'mac'), default='mac', | |
|
TVL
2016/06/03 15:49:11
Why not "iOS" and "Mac" so you don't have to do a
sdefresne
2016/06/03 16:11:45
This value will be passed from BUILD.gn or *.gyp f
TVL
2016/06/03 16:24:27
That's fair, minor nit then: you might want to do
sdefresne
2016/06/03 16:29:04
Done.
| |
| 252 help='The target platform of the bundle') | |
| 253 parser.add_option('--version-overrides', action='append', | |
| 254 help='Key-value pair to override specific component of version') | |
|
TVL
2016/06/03 15:49:11
Like --version does, you probably want to give an
sdefresne
2016/06/03 16:11:45
Done.
| |
| 255 parser.add_option('--format', choices=('binary1', 'xml1', 'json'), | |
| 256 default='xml1', help='Format to use when writing property list') | |
| 223 parser.add_option('--version', dest='version', action='store', type='string', | 257 parser.add_option('--version', dest='version', action='store', type='string', |
| 224 default=None, help='The version string [major.minor.build.patch]') | 258 default=None, help='The version string [major.minor.build.patch]') |
| 225 (options, args) = parser.parse_args(argv) | 259 (options, args) = parser.parse_args(argv) |
| 226 | 260 |
| 227 if len(args) > 0: | 261 if len(args) > 0: |
| 228 print >>sys.stderr, parser.get_usage() | 262 print >>sys.stderr, parser.get_usage() |
| 229 return 1 | 263 return 1 |
| 230 | 264 |
| 231 if not options.plist_path: | 265 if not options.plist_path: |
| 232 print >>sys.stderr, 'No --plist specified.' | 266 print >>sys.stderr, 'No --plist specified.' |
| 233 return 1 | 267 return 1 |
| 234 | 268 |
| 235 # Read the plist into its parsed format. | 269 # Read the plist into its parsed format. Convert the file to 'xml1' as |
| 236 plist = plistlib.readPlist(options.plist_path) | 270 # plistlib only supports that format in Python 2.7. |
| 271 with tempfile.NamedTemporaryFile() as temp_info_plist: | |
| 272 retcode = _ConvertPlist(options.plist_path, temp_info_plist.name, 'xml1') | |
| 273 if retcode != 0: | |
| 274 return retcode | |
| 275 plist = plistlib.readPlist(temp_info_plist.name) | |
| 276 | |
| 277 # Convert overrides. | |
| 278 overrides = {} | |
| 279 if options.version_overrides: | |
| 280 for pair in options.version_overrides: | |
| 281 if not '=' in pair: | |
| 282 print >>sys.stderr, 'Invalid value for --version-overrides:', pair | |
| 283 return 1 | |
| 284 key, value = pair.split('=', 1) | |
| 285 overrides[key] = value | |
|
TVL
2016/06/03 15:49:11
Do you want to validate the key to ensure it is on
sdefresne
2016/06/03 16:11:45
Done.
| |
| 237 | 286 |
| 238 # Insert the product version. | 287 # Insert the product version. |
| 239 if not _AddVersionKeys(plist, version=options.version): | 288 if not _AddVersionKeys(plist, version=options.version, overrides=overrides): |
| 240 return 2 | 289 return 2 |
| 241 | 290 |
| 242 # Add Breakpad if configured to do so. | 291 # Add Breakpad if configured to do so. |
| 243 if options.use_breakpad: | 292 if options.use_breakpad: |
| 244 if options.branding is None: | 293 if options.branding is None: |
| 245 print >>sys.stderr, 'Use of Breakpad requires branding.' | 294 print >>sys.stderr, 'Use of Breakpad requires branding.' |
| 246 return 1 | 295 return 1 |
| 247 _AddBreakpadKeys(plist, options.branding) | 296 _AddBreakpadKeys(plist, options.branding, options.platform) |
| 248 if options.breakpad_uploads: | 297 if options.breakpad_uploads: |
| 249 plist['BreakpadURL'] = 'https://clients2.google.com/cr/report' | 298 plist['BreakpadURL'] = 'https://clients2.google.com/cr/report' |
| 250 else: | 299 else: |
| 251 # This allows crash dumping to a file without uploading the | 300 # This allows crash dumping to a file without uploading the |
| 252 # dump, for testing purposes. Breakpad does not recognise | 301 # dump, for testing purposes. Breakpad does not recognise |
| 253 # "none" as a special value, but this does stop crash dump | 302 # "none" as a special value, but this does stop crash dump |
| 254 # uploading from happening. We need to specify something | 303 # uploading from happening. We need to specify something |
| 255 # because if "BreakpadURL" is not present, Breakpad will not | 304 # because if "BreakpadURL" is not present, Breakpad will not |
| 256 # register its crash handler and no crash dumping will occur. | 305 # register its crash handler and no crash dumping will occur. |
| 257 plist['BreakpadURL'] = 'none' | 306 plist['BreakpadURL'] = 'none' |
| 258 else: | 307 else: |
| 259 _RemoveBreakpadKeys(plist) | 308 _RemoveBreakpadKeys(plist) |
| 260 | 309 |
| 261 # Add Keystone if configured to do so. | 310 # Add Keystone if configured to do so. |
| 262 if options.use_keystone: | 311 if options.use_keystone: |
| 263 if options.bundle_identifier is None: | 312 if options.bundle_identifier is None: |
| 264 print >>sys.stderr, 'Use of Keystone requires the bundle id.' | 313 print >>sys.stderr, 'Use of Keystone requires the bundle id.' |
| 265 return 1 | 314 return 1 |
| 266 _AddKeystoneKeys(plist, options.bundle_identifier) | 315 _AddKeystoneKeys(plist, options.bundle_identifier) |
| 267 else: | 316 else: |
| 268 _RemoveKeystoneKeys(plist) | 317 _RemoveKeystoneKeys(plist) |
| 269 | 318 |
| 270 # Adds or removes any SCM keys. | 319 # Adds or removes any SCM keys. |
| 271 if not _DoSCMKeys(plist, options.add_scm_info): | 320 if not _DoSCMKeys(plist, options.add_scm_info): |
| 272 return 3 | 321 return 3 |
| 273 | 322 |
| 274 # Now that all keys have been mutated, rewrite the file. | |
| 275 temp_info_plist = tempfile.NamedTemporaryFile() | |
| 276 plistlib.writePlist(plist, temp_info_plist.name) | |
| 277 | |
| 278 # Info.plist will work perfectly well in any plist format, but traditionally | |
| 279 # applications use xml1 for this, so convert it to ensure that it's valid. | |
| 280 output_path = options.plist_path | 323 output_path = options.plist_path |
| 281 if options.plist_output is not None: | 324 if options.plist_output is not None: |
| 282 output_path = options.plist_output | 325 output_path = options.plist_output |
| 283 proc = subprocess.Popen(['plutil', '-convert', 'xml1', | 326 |
| 284 '-o', output_path, | 327 # Now that all keys have been mutated, rewrite the file. |
| 285 temp_info_plist.name]) | 328 with tempfile.NamedTemporaryFile() as temp_info_plist: |
| 286 proc.wait() | 329 plistlib.writePlist(plist, temp_info_plist.name) |
| 287 return proc.returncode | 330 |
| 331 # Info.plist will work perfectly well in any plist format, but traditionally | |
| 332 # applications use xml1 for this, so convert it to ensure that it's valid. | |
|
TVL
2016/06/03 15:49:11
Comment isn't completely right, you are honoring t
sdefresne
2016/06/03 16:11:45
Done.
| |
| 333 return _ConvertPlist(temp_info_plist.name, output_path, options.format) | |
| 288 | 334 |
| 289 | 335 |
| 290 if __name__ == '__main__': | 336 if __name__ == '__main__': |
| 291 sys.exit(Main(sys.argv[1:])) | 337 sys.exit(Main(sys.argv[1:])) |
| OLD | NEW |