media/webrtc/trunk/build/mac/tweak_info_plist.py

changeset 0
6474c204b198
     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:]))

mercurial