xref: /aosp_15_r20/external/cronet/build/config/ios/codesign.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker# Copyright 2016 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker
6*6777b538SAndroid Build Coastguard Workerimport argparse
7*6777b538SAndroid Build Coastguard Workerimport codecs
8*6777b538SAndroid Build Coastguard Workerimport datetime
9*6777b538SAndroid Build Coastguard Workerimport fnmatch
10*6777b538SAndroid Build Coastguard Workerimport glob
11*6777b538SAndroid Build Coastguard Workerimport json
12*6777b538SAndroid Build Coastguard Workerimport os
13*6777b538SAndroid Build Coastguard Workerimport plistlib
14*6777b538SAndroid Build Coastguard Workerimport shutil
15*6777b538SAndroid Build Coastguard Workerimport subprocess
16*6777b538SAndroid Build Coastguard Workerimport stat
17*6777b538SAndroid Build Coastguard Workerimport sys
18*6777b538SAndroid Build Coastguard Workerimport tempfile
19*6777b538SAndroid Build Coastguard Worker
20*6777b538SAndroid Build Coastguard Workerif sys.version_info.major < 3:
21*6777b538SAndroid Build Coastguard Worker  basestring_compat = basestring
22*6777b538SAndroid Build Coastguard Workerelse:
23*6777b538SAndroid Build Coastguard Worker  basestring_compat = str
24*6777b538SAndroid Build Coastguard Worker
25*6777b538SAndroid Build Coastguard Worker
26*6777b538SAndroid Build Coastguard Workerdef GetProvisioningProfilesDir():
27*6777b538SAndroid Build Coastguard Worker  """Returns the location of the installed mobile provisioning profiles.
28*6777b538SAndroid Build Coastguard Worker
29*6777b538SAndroid Build Coastguard Worker  Returns:
30*6777b538SAndroid Build Coastguard Worker    The path to the directory containing the installed mobile provisioning
31*6777b538SAndroid Build Coastguard Worker    profiles as a string.
32*6777b538SAndroid Build Coastguard Worker  """
33*6777b538SAndroid Build Coastguard Worker  return os.path.join(
34*6777b538SAndroid Build Coastguard Worker      os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles')
35*6777b538SAndroid Build Coastguard Worker
36*6777b538SAndroid Build Coastguard Worker
37*6777b538SAndroid Build Coastguard Workerdef ReadPlistFromString(plist_bytes):
38*6777b538SAndroid Build Coastguard Worker  """Parse property list from given |plist_bytes|.
39*6777b538SAndroid Build Coastguard Worker
40*6777b538SAndroid Build Coastguard Worker    Args:
41*6777b538SAndroid Build Coastguard Worker      plist_bytes: contents of property list to load. Must be bytes in python 3.
42*6777b538SAndroid Build Coastguard Worker
43*6777b538SAndroid Build Coastguard Worker    Returns:
44*6777b538SAndroid Build Coastguard Worker      The contents of property list as a python object.
45*6777b538SAndroid Build Coastguard Worker    """
46*6777b538SAndroid Build Coastguard Worker  if sys.version_info.major == 2:
47*6777b538SAndroid Build Coastguard Worker    return plistlib.readPlistFromString(plist_bytes)
48*6777b538SAndroid Build Coastguard Worker  else:
49*6777b538SAndroid Build Coastguard Worker    return plistlib.loads(plist_bytes)
50*6777b538SAndroid Build Coastguard Worker
51*6777b538SAndroid Build Coastguard Worker
52*6777b538SAndroid Build Coastguard Workerdef LoadPlistFile(plist_path):
53*6777b538SAndroid Build Coastguard Worker  """Loads property list file at |plist_path|.
54*6777b538SAndroid Build Coastguard Worker
55*6777b538SAndroid Build Coastguard Worker  Args:
56*6777b538SAndroid Build Coastguard Worker    plist_path: path to the property list file to load.
57*6777b538SAndroid Build Coastguard Worker
58*6777b538SAndroid Build Coastguard Worker  Returns:
59*6777b538SAndroid Build Coastguard Worker    The content of the property list file as a python object.
60*6777b538SAndroid Build Coastguard Worker  """
61*6777b538SAndroid Build Coastguard Worker  if sys.version_info.major == 2:
62*6777b538SAndroid Build Coastguard Worker    return plistlib.readPlistFromString(
63*6777b538SAndroid Build Coastguard Worker        subprocess.check_output(
64*6777b538SAndroid Build Coastguard Worker            ['xcrun', 'plutil', '-convert', 'xml1', '-o', '-', plist_path]))
65*6777b538SAndroid Build Coastguard Worker  else:
66*6777b538SAndroid Build Coastguard Worker    with open(plist_path, 'rb') as fp:
67*6777b538SAndroid Build Coastguard Worker      return plistlib.load(fp)
68*6777b538SAndroid Build Coastguard Worker
69*6777b538SAndroid Build Coastguard Worker
70*6777b538SAndroid Build Coastguard Workerdef CreateSymlink(value, location):
71*6777b538SAndroid Build Coastguard Worker  """Creates symlink with value at location if the target exists."""
72*6777b538SAndroid Build Coastguard Worker  target = os.path.join(os.path.dirname(location), value)
73*6777b538SAndroid Build Coastguard Worker  if os.path.exists(location):
74*6777b538SAndroid Build Coastguard Worker    os.unlink(location)
75*6777b538SAndroid Build Coastguard Worker  os.symlink(value, location)
76*6777b538SAndroid Build Coastguard Worker
77*6777b538SAndroid Build Coastguard Worker
78*6777b538SAndroid Build Coastguard Workerclass Bundle(object):
79*6777b538SAndroid Build Coastguard Worker  """Wraps a bundle."""
80*6777b538SAndroid Build Coastguard Worker
81*6777b538SAndroid Build Coastguard Worker  def __init__(self, bundle_path, platform):
82*6777b538SAndroid Build Coastguard Worker    """Initializes the Bundle object with data from bundle Info.plist file."""
83*6777b538SAndroid Build Coastguard Worker    self._path = bundle_path
84*6777b538SAndroid Build Coastguard Worker    self._kind = Bundle.Kind(platform, os.path.splitext(bundle_path)[-1])
85*6777b538SAndroid Build Coastguard Worker    self._data = None
86*6777b538SAndroid Build Coastguard Worker
87*6777b538SAndroid Build Coastguard Worker  def Load(self):
88*6777b538SAndroid Build Coastguard Worker    self._data = LoadPlistFile(self.info_plist_path)
89*6777b538SAndroid Build Coastguard Worker
90*6777b538SAndroid Build Coastguard Worker  @staticmethod
91*6777b538SAndroid Build Coastguard Worker  def Kind(platform, extension):
92*6777b538SAndroid Build Coastguard Worker    if platform == 'iphonesimulator' or platform == 'iphoneos':
93*6777b538SAndroid Build Coastguard Worker      return 'ios'
94*6777b538SAndroid Build Coastguard Worker    if platform == 'macosx':
95*6777b538SAndroid Build Coastguard Worker      if extension == '.framework':
96*6777b538SAndroid Build Coastguard Worker        return 'mac_framework'
97*6777b538SAndroid Build Coastguard Worker      return 'mac'
98*6777b538SAndroid Build Coastguard Worker    raise ValueError('unknown bundle type %s for %s' % (extension, platform))
99*6777b538SAndroid Build Coastguard Worker
100*6777b538SAndroid Build Coastguard Worker  @property
101*6777b538SAndroid Build Coastguard Worker  def kind(self):
102*6777b538SAndroid Build Coastguard Worker    return self._kind
103*6777b538SAndroid Build Coastguard Worker
104*6777b538SAndroid Build Coastguard Worker  @property
105*6777b538SAndroid Build Coastguard Worker  def path(self):
106*6777b538SAndroid Build Coastguard Worker    return self._path
107*6777b538SAndroid Build Coastguard Worker
108*6777b538SAndroid Build Coastguard Worker  @property
109*6777b538SAndroid Build Coastguard Worker  def contents_dir(self):
110*6777b538SAndroid Build Coastguard Worker    if self._kind == 'mac':
111*6777b538SAndroid Build Coastguard Worker      return os.path.join(self.path, 'Contents')
112*6777b538SAndroid Build Coastguard Worker    if self._kind == 'mac_framework':
113*6777b538SAndroid Build Coastguard Worker      return os.path.join(self.path, 'Versions/A')
114*6777b538SAndroid Build Coastguard Worker    return self.path
115*6777b538SAndroid Build Coastguard Worker
116*6777b538SAndroid Build Coastguard Worker  @property
117*6777b538SAndroid Build Coastguard Worker  def executable_dir(self):
118*6777b538SAndroid Build Coastguard Worker    if self._kind == 'mac':
119*6777b538SAndroid Build Coastguard Worker      return os.path.join(self.contents_dir, 'MacOS')
120*6777b538SAndroid Build Coastguard Worker    return self.contents_dir
121*6777b538SAndroid Build Coastguard Worker
122*6777b538SAndroid Build Coastguard Worker  @property
123*6777b538SAndroid Build Coastguard Worker  def resources_dir(self):
124*6777b538SAndroid Build Coastguard Worker    if self._kind == 'mac' or self._kind == 'mac_framework':
125*6777b538SAndroid Build Coastguard Worker      return os.path.join(self.contents_dir, 'Resources')
126*6777b538SAndroid Build Coastguard Worker    return self.path
127*6777b538SAndroid Build Coastguard Worker
128*6777b538SAndroid Build Coastguard Worker  @property
129*6777b538SAndroid Build Coastguard Worker  def info_plist_path(self):
130*6777b538SAndroid Build Coastguard Worker    if self._kind == 'mac_framework':
131*6777b538SAndroid Build Coastguard Worker      return os.path.join(self.resources_dir, 'Info.plist')
132*6777b538SAndroid Build Coastguard Worker    return os.path.join(self.contents_dir, 'Info.plist')
133*6777b538SAndroid Build Coastguard Worker
134*6777b538SAndroid Build Coastguard Worker  @property
135*6777b538SAndroid Build Coastguard Worker  def signature_dir(self):
136*6777b538SAndroid Build Coastguard Worker    return os.path.join(self.contents_dir, '_CodeSignature')
137*6777b538SAndroid Build Coastguard Worker
138*6777b538SAndroid Build Coastguard Worker  @property
139*6777b538SAndroid Build Coastguard Worker  def identifier(self):
140*6777b538SAndroid Build Coastguard Worker    return self._data['CFBundleIdentifier']
141*6777b538SAndroid Build Coastguard Worker
142*6777b538SAndroid Build Coastguard Worker  @property
143*6777b538SAndroid Build Coastguard Worker  def binary_name(self):
144*6777b538SAndroid Build Coastguard Worker    return self._data['CFBundleExecutable']
145*6777b538SAndroid Build Coastguard Worker
146*6777b538SAndroid Build Coastguard Worker  @property
147*6777b538SAndroid Build Coastguard Worker  def binary_path(self):
148*6777b538SAndroid Build Coastguard Worker    return os.path.join(self.executable_dir, self.binary_name)
149*6777b538SAndroid Build Coastguard Worker
150*6777b538SAndroid Build Coastguard Worker  def Validate(self, expected_mappings):
151*6777b538SAndroid Build Coastguard Worker    """Checks that keys in the bundle have the expected value.
152*6777b538SAndroid Build Coastguard Worker
153*6777b538SAndroid Build Coastguard Worker    Args:
154*6777b538SAndroid Build Coastguard Worker      expected_mappings: a dictionary of string to object, each mapping will
155*6777b538SAndroid Build Coastguard Worker      be looked up in the bundle data to check it has the same value (missing
156*6777b538SAndroid Build Coastguard Worker      values will be ignored)
157*6777b538SAndroid Build Coastguard Worker
158*6777b538SAndroid Build Coastguard Worker    Returns:
159*6777b538SAndroid Build Coastguard Worker      A dictionary of the key with a different value between expected_mappings
160*6777b538SAndroid Build Coastguard Worker      and the content of the bundle (i.e. errors) so that caller can format the
161*6777b538SAndroid Build Coastguard Worker      error message. The dictionary will be empty if there are no errors.
162*6777b538SAndroid Build Coastguard Worker    """
163*6777b538SAndroid Build Coastguard Worker    errors = {}
164*6777b538SAndroid Build Coastguard Worker    for key, expected_value in expected_mappings.items():
165*6777b538SAndroid Build Coastguard Worker      if key in self._data:
166*6777b538SAndroid Build Coastguard Worker        value = self._data[key]
167*6777b538SAndroid Build Coastguard Worker        if value != expected_value:
168*6777b538SAndroid Build Coastguard Worker          errors[key] = (value, expected_value)
169*6777b538SAndroid Build Coastguard Worker    return errors
170*6777b538SAndroid Build Coastguard Worker
171*6777b538SAndroid Build Coastguard Worker
172*6777b538SAndroid Build Coastguard Workerclass ProvisioningProfile(object):
173*6777b538SAndroid Build Coastguard Worker  """Wraps a mobile provisioning profile file."""
174*6777b538SAndroid Build Coastguard Worker
175*6777b538SAndroid Build Coastguard Worker  def __init__(self, provisioning_profile_path):
176*6777b538SAndroid Build Coastguard Worker    """Initializes the ProvisioningProfile with data from profile file."""
177*6777b538SAndroid Build Coastguard Worker    self._path = provisioning_profile_path
178*6777b538SAndroid Build Coastguard Worker    self._data = ReadPlistFromString(
179*6777b538SAndroid Build Coastguard Worker        subprocess.check_output([
180*6777b538SAndroid Build Coastguard Worker            'xcrun', 'security', 'cms', '-D', '-u', 'certUsageAnyCA', '-i',
181*6777b538SAndroid Build Coastguard Worker            provisioning_profile_path
182*6777b538SAndroid Build Coastguard Worker        ]))
183*6777b538SAndroid Build Coastguard Worker
184*6777b538SAndroid Build Coastguard Worker  @property
185*6777b538SAndroid Build Coastguard Worker  def path(self):
186*6777b538SAndroid Build Coastguard Worker    return self._path
187*6777b538SAndroid Build Coastguard Worker
188*6777b538SAndroid Build Coastguard Worker  @property
189*6777b538SAndroid Build Coastguard Worker  def team_identifier(self):
190*6777b538SAndroid Build Coastguard Worker    return self._data.get('TeamIdentifier', [''])[0]
191*6777b538SAndroid Build Coastguard Worker
192*6777b538SAndroid Build Coastguard Worker  @property
193*6777b538SAndroid Build Coastguard Worker  def name(self):
194*6777b538SAndroid Build Coastguard Worker    return self._data.get('Name', '')
195*6777b538SAndroid Build Coastguard Worker
196*6777b538SAndroid Build Coastguard Worker  @property
197*6777b538SAndroid Build Coastguard Worker  def application_identifier_pattern(self):
198*6777b538SAndroid Build Coastguard Worker    return self._data.get('Entitlements', {}).get('application-identifier', '')
199*6777b538SAndroid Build Coastguard Worker
200*6777b538SAndroid Build Coastguard Worker  @property
201*6777b538SAndroid Build Coastguard Worker  def application_identifier_prefix(self):
202*6777b538SAndroid Build Coastguard Worker    return self._data.get('ApplicationIdentifierPrefix', [''])[0]
203*6777b538SAndroid Build Coastguard Worker
204*6777b538SAndroid Build Coastguard Worker  @property
205*6777b538SAndroid Build Coastguard Worker  def entitlements(self):
206*6777b538SAndroid Build Coastguard Worker    return self._data.get('Entitlements', {})
207*6777b538SAndroid Build Coastguard Worker
208*6777b538SAndroid Build Coastguard Worker  @property
209*6777b538SAndroid Build Coastguard Worker  def expiration_date(self):
210*6777b538SAndroid Build Coastguard Worker    return self._data.get('ExpirationDate', datetime.datetime.now())
211*6777b538SAndroid Build Coastguard Worker
212*6777b538SAndroid Build Coastguard Worker  def ValidToSignBundle(self, bundle_identifier):
213*6777b538SAndroid Build Coastguard Worker    """Checks whether the provisioning profile can sign bundle_identifier.
214*6777b538SAndroid Build Coastguard Worker
215*6777b538SAndroid Build Coastguard Worker    Args:
216*6777b538SAndroid Build Coastguard Worker      bundle_identifier: the identifier of the bundle that needs to be signed.
217*6777b538SAndroid Build Coastguard Worker
218*6777b538SAndroid Build Coastguard Worker    Returns:
219*6777b538SAndroid Build Coastguard Worker      True if the mobile provisioning profile can be used to sign a bundle
220*6777b538SAndroid Build Coastguard Worker      with the corresponding bundle_identifier, False otherwise.
221*6777b538SAndroid Build Coastguard Worker    """
222*6777b538SAndroid Build Coastguard Worker    return fnmatch.fnmatch(
223*6777b538SAndroid Build Coastguard Worker        '%s.%s' % (self.application_identifier_prefix, bundle_identifier),
224*6777b538SAndroid Build Coastguard Worker        self.application_identifier_pattern)
225*6777b538SAndroid Build Coastguard Worker
226*6777b538SAndroid Build Coastguard Worker  def Install(self, installation_path):
227*6777b538SAndroid Build Coastguard Worker    """Copies mobile provisioning profile info to |installation_path|."""
228*6777b538SAndroid Build Coastguard Worker    shutil.copy2(self.path, installation_path)
229*6777b538SAndroid Build Coastguard Worker    st = os.stat(installation_path)
230*6777b538SAndroid Build Coastguard Worker    os.chmod(installation_path, st.st_mode | stat.S_IWUSR)
231*6777b538SAndroid Build Coastguard Worker
232*6777b538SAndroid Build Coastguard Worker
233*6777b538SAndroid Build Coastguard Workerclass Entitlements(object):
234*6777b538SAndroid Build Coastguard Worker  """Wraps an Entitlement plist file."""
235*6777b538SAndroid Build Coastguard Worker
236*6777b538SAndroid Build Coastguard Worker  def __init__(self, entitlements_path):
237*6777b538SAndroid Build Coastguard Worker    """Initializes Entitlements object from entitlement file."""
238*6777b538SAndroid Build Coastguard Worker    self._path = entitlements_path
239*6777b538SAndroid Build Coastguard Worker    self._data = LoadPlistFile(self._path)
240*6777b538SAndroid Build Coastguard Worker
241*6777b538SAndroid Build Coastguard Worker  @property
242*6777b538SAndroid Build Coastguard Worker  def path(self):
243*6777b538SAndroid Build Coastguard Worker    return self._path
244*6777b538SAndroid Build Coastguard Worker
245*6777b538SAndroid Build Coastguard Worker  def ExpandVariables(self, substitutions):
246*6777b538SAndroid Build Coastguard Worker    self._data = self._ExpandVariables(self._data, substitutions)
247*6777b538SAndroid Build Coastguard Worker
248*6777b538SAndroid Build Coastguard Worker  def _ExpandVariables(self, data, substitutions):
249*6777b538SAndroid Build Coastguard Worker    if isinstance(data, basestring_compat):
250*6777b538SAndroid Build Coastguard Worker      for key, substitution in substitutions.items():
251*6777b538SAndroid Build Coastguard Worker        data = data.replace('$(%s)' % (key,), substitution)
252*6777b538SAndroid Build Coastguard Worker      return data
253*6777b538SAndroid Build Coastguard Worker
254*6777b538SAndroid Build Coastguard Worker    if isinstance(data, dict):
255*6777b538SAndroid Build Coastguard Worker      for key, value in data.items():
256*6777b538SAndroid Build Coastguard Worker        data[key] = self._ExpandVariables(value, substitutions)
257*6777b538SAndroid Build Coastguard Worker      return data
258*6777b538SAndroid Build Coastguard Worker
259*6777b538SAndroid Build Coastguard Worker    if isinstance(data, list):
260*6777b538SAndroid Build Coastguard Worker      for i, value in enumerate(data):
261*6777b538SAndroid Build Coastguard Worker        data[i] = self._ExpandVariables(value, substitutions)
262*6777b538SAndroid Build Coastguard Worker
263*6777b538SAndroid Build Coastguard Worker    return data
264*6777b538SAndroid Build Coastguard Worker
265*6777b538SAndroid Build Coastguard Worker  def LoadDefaults(self, defaults):
266*6777b538SAndroid Build Coastguard Worker    for key, value in defaults.items():
267*6777b538SAndroid Build Coastguard Worker      if key not in self._data:
268*6777b538SAndroid Build Coastguard Worker        self._data[key] = value
269*6777b538SAndroid Build Coastguard Worker
270*6777b538SAndroid Build Coastguard Worker  def WriteTo(self, target_path):
271*6777b538SAndroid Build Coastguard Worker    with open(target_path, 'wb') as fp:
272*6777b538SAndroid Build Coastguard Worker      if sys.version_info.major == 2:
273*6777b538SAndroid Build Coastguard Worker        plistlib.writePlist(self._data, fp)
274*6777b538SAndroid Build Coastguard Worker      else:
275*6777b538SAndroid Build Coastguard Worker        plistlib.dump(self._data, fp)
276*6777b538SAndroid Build Coastguard Worker
277*6777b538SAndroid Build Coastguard Worker
278*6777b538SAndroid Build Coastguard Workerdef FindProvisioningProfile(provisioning_profile_paths, bundle_identifier,
279*6777b538SAndroid Build Coastguard Worker                            required):
280*6777b538SAndroid Build Coastguard Worker  """Finds mobile provisioning profile to use to sign bundle.
281*6777b538SAndroid Build Coastguard Worker
282*6777b538SAndroid Build Coastguard Worker  Args:
283*6777b538SAndroid Build Coastguard Worker    bundle_identifier: the identifier of the bundle to sign.
284*6777b538SAndroid Build Coastguard Worker
285*6777b538SAndroid Build Coastguard Worker  Returns:
286*6777b538SAndroid Build Coastguard Worker    The ProvisioningProfile object that can be used to sign the Bundle
287*6777b538SAndroid Build Coastguard Worker    object or None if no matching provisioning profile was found.
288*6777b538SAndroid Build Coastguard Worker  """
289*6777b538SAndroid Build Coastguard Worker  if not provisioning_profile_paths:
290*6777b538SAndroid Build Coastguard Worker    provisioning_profile_paths = glob.glob(
291*6777b538SAndroid Build Coastguard Worker        os.path.join(GetProvisioningProfilesDir(), '*.mobileprovision'))
292*6777b538SAndroid Build Coastguard Worker
293*6777b538SAndroid Build Coastguard Worker  # Iterate over all installed mobile provisioning profiles and filter those
294*6777b538SAndroid Build Coastguard Worker  # that can be used to sign the bundle, ignoring expired ones.
295*6777b538SAndroid Build Coastguard Worker  now = datetime.datetime.now()
296*6777b538SAndroid Build Coastguard Worker  valid_provisioning_profiles = []
297*6777b538SAndroid Build Coastguard Worker  one_hour = datetime.timedelta(0, 3600)
298*6777b538SAndroid Build Coastguard Worker  for provisioning_profile_path in provisioning_profile_paths:
299*6777b538SAndroid Build Coastguard Worker    provisioning_profile = ProvisioningProfile(provisioning_profile_path)
300*6777b538SAndroid Build Coastguard Worker    if provisioning_profile.expiration_date - now < one_hour:
301*6777b538SAndroid Build Coastguard Worker      sys.stderr.write(
302*6777b538SAndroid Build Coastguard Worker          'Warning: ignoring expired provisioning profile: %s.\n' %
303*6777b538SAndroid Build Coastguard Worker          provisioning_profile_path)
304*6777b538SAndroid Build Coastguard Worker      continue
305*6777b538SAndroid Build Coastguard Worker    if provisioning_profile.ValidToSignBundle(bundle_identifier):
306*6777b538SAndroid Build Coastguard Worker      valid_provisioning_profiles.append(provisioning_profile)
307*6777b538SAndroid Build Coastguard Worker
308*6777b538SAndroid Build Coastguard Worker  if not valid_provisioning_profiles:
309*6777b538SAndroid Build Coastguard Worker    if required:
310*6777b538SAndroid Build Coastguard Worker      sys.stderr.write(
311*6777b538SAndroid Build Coastguard Worker          'Error: no mobile provisioning profile found for "%s" in %s.\n' %
312*6777b538SAndroid Build Coastguard Worker          (bundle_identifier, provisioning_profile_paths))
313*6777b538SAndroid Build Coastguard Worker      sys.exit(1)
314*6777b538SAndroid Build Coastguard Worker    return None
315*6777b538SAndroid Build Coastguard Worker
316*6777b538SAndroid Build Coastguard Worker  # Select the most specific mobile provisioning profile, i.e. the one with
317*6777b538SAndroid Build Coastguard Worker  # the longest application identifier pattern (prefer the one with the latest
318*6777b538SAndroid Build Coastguard Worker  # expiration date as a secondary criteria).
319*6777b538SAndroid Build Coastguard Worker  selected_provisioning_profile = max(
320*6777b538SAndroid Build Coastguard Worker      valid_provisioning_profiles,
321*6777b538SAndroid Build Coastguard Worker      key=lambda p: (len(p.application_identifier_pattern), p.expiration_date))
322*6777b538SAndroid Build Coastguard Worker
323*6777b538SAndroid Build Coastguard Worker  one_week = datetime.timedelta(7)
324*6777b538SAndroid Build Coastguard Worker  if selected_provisioning_profile.expiration_date - now < 2 * one_week:
325*6777b538SAndroid Build Coastguard Worker    sys.stderr.write(
326*6777b538SAndroid Build Coastguard Worker        'Warning: selected provisioning profile will expire soon: %s' %
327*6777b538SAndroid Build Coastguard Worker        selected_provisioning_profile.path)
328*6777b538SAndroid Build Coastguard Worker  return selected_provisioning_profile
329*6777b538SAndroid Build Coastguard Worker
330*6777b538SAndroid Build Coastguard Worker
331*6777b538SAndroid Build Coastguard Workerdef CodeSignBundle(bundle_path, identity, extra_args):
332*6777b538SAndroid Build Coastguard Worker  process = subprocess.Popen(
333*6777b538SAndroid Build Coastguard Worker      ['xcrun', 'codesign', '--force', '--sign', identity, '--timestamp=none'] +
334*6777b538SAndroid Build Coastguard Worker      list(extra_args) + [bundle_path],
335*6777b538SAndroid Build Coastguard Worker      stderr=subprocess.PIPE,
336*6777b538SAndroid Build Coastguard Worker      universal_newlines=True)
337*6777b538SAndroid Build Coastguard Worker  _, stderr = process.communicate()
338*6777b538SAndroid Build Coastguard Worker  if process.returncode:
339*6777b538SAndroid Build Coastguard Worker    sys.stderr.write(stderr)
340*6777b538SAndroid Build Coastguard Worker    sys.exit(process.returncode)
341*6777b538SAndroid Build Coastguard Worker  for line in stderr.splitlines():
342*6777b538SAndroid Build Coastguard Worker    if line.endswith(': replacing existing signature'):
343*6777b538SAndroid Build Coastguard Worker      # Ignore warning about replacing existing signature as this should only
344*6777b538SAndroid Build Coastguard Worker      # happen when re-signing system frameworks (and then it is expected).
345*6777b538SAndroid Build Coastguard Worker      continue
346*6777b538SAndroid Build Coastguard Worker    sys.stderr.write(line)
347*6777b538SAndroid Build Coastguard Worker    sys.stderr.write('\n')
348*6777b538SAndroid Build Coastguard Worker
349*6777b538SAndroid Build Coastguard Worker
350*6777b538SAndroid Build Coastguard Workerdef InstallSystemFramework(framework_path, bundle_path, args):
351*6777b538SAndroid Build Coastguard Worker  """Install framework from |framework_path| to |bundle| and code-re-sign it."""
352*6777b538SAndroid Build Coastguard Worker  installed_framework_path = os.path.join(
353*6777b538SAndroid Build Coastguard Worker      bundle_path, 'Frameworks', os.path.basename(framework_path))
354*6777b538SAndroid Build Coastguard Worker
355*6777b538SAndroid Build Coastguard Worker  if os.path.isfile(framework_path):
356*6777b538SAndroid Build Coastguard Worker    shutil.copy(framework_path, installed_framework_path)
357*6777b538SAndroid Build Coastguard Worker  elif os.path.isdir(framework_path):
358*6777b538SAndroid Build Coastguard Worker    if os.path.exists(installed_framework_path):
359*6777b538SAndroid Build Coastguard Worker      shutil.rmtree(installed_framework_path)
360*6777b538SAndroid Build Coastguard Worker    shutil.copytree(framework_path, installed_framework_path)
361*6777b538SAndroid Build Coastguard Worker
362*6777b538SAndroid Build Coastguard Worker  CodeSignBundle(installed_framework_path, args.identity,
363*6777b538SAndroid Build Coastguard Worker      ['--deep', '--preserve-metadata=identifier,entitlements,flags'])
364*6777b538SAndroid Build Coastguard Worker
365*6777b538SAndroid Build Coastguard Worker
366*6777b538SAndroid Build Coastguard Workerdef GenerateEntitlements(path, provisioning_profile, bundle_identifier):
367*6777b538SAndroid Build Coastguard Worker  """Generates an entitlements file.
368*6777b538SAndroid Build Coastguard Worker
369*6777b538SAndroid Build Coastguard Worker  Args:
370*6777b538SAndroid Build Coastguard Worker    path: path to the entitlements template file
371*6777b538SAndroid Build Coastguard Worker    provisioning_profile: ProvisioningProfile object to use, may be None
372*6777b538SAndroid Build Coastguard Worker    bundle_identifier: identifier of the bundle to sign.
373*6777b538SAndroid Build Coastguard Worker  """
374*6777b538SAndroid Build Coastguard Worker  entitlements = Entitlements(path)
375*6777b538SAndroid Build Coastguard Worker  if provisioning_profile:
376*6777b538SAndroid Build Coastguard Worker    entitlements.LoadDefaults(provisioning_profile.entitlements)
377*6777b538SAndroid Build Coastguard Worker    app_identifier_prefix = \
378*6777b538SAndroid Build Coastguard Worker      provisioning_profile.application_identifier_prefix + '.'
379*6777b538SAndroid Build Coastguard Worker  else:
380*6777b538SAndroid Build Coastguard Worker    app_identifier_prefix = '*.'
381*6777b538SAndroid Build Coastguard Worker  entitlements.ExpandVariables({
382*6777b538SAndroid Build Coastguard Worker      'CFBundleIdentifier': bundle_identifier,
383*6777b538SAndroid Build Coastguard Worker      'AppIdentifierPrefix': app_identifier_prefix,
384*6777b538SAndroid Build Coastguard Worker  })
385*6777b538SAndroid Build Coastguard Worker  return entitlements
386*6777b538SAndroid Build Coastguard Worker
387*6777b538SAndroid Build Coastguard Worker
388*6777b538SAndroid Build Coastguard Workerdef GenerateBundleInfoPlist(bundle, plist_compiler, partial_plist):
389*6777b538SAndroid Build Coastguard Worker  """Generates the bundle Info.plist for a list of partial .plist files.
390*6777b538SAndroid Build Coastguard Worker
391*6777b538SAndroid Build Coastguard Worker  Args:
392*6777b538SAndroid Build Coastguard Worker    bundle: a Bundle instance
393*6777b538SAndroid Build Coastguard Worker    plist_compiler: string, path to the Info.plist compiler
394*6777b538SAndroid Build Coastguard Worker    partial_plist: list of path to partial .plist files to merge
395*6777b538SAndroid Build Coastguard Worker  """
396*6777b538SAndroid Build Coastguard Worker
397*6777b538SAndroid Build Coastguard Worker  # Filter empty partial .plist files (this happens if an application
398*6777b538SAndroid Build Coastguard Worker  # does not compile any asset catalog, in which case the partial .plist
399*6777b538SAndroid Build Coastguard Worker  # file from the asset catalog compilation step is just a stamp file).
400*6777b538SAndroid Build Coastguard Worker  filtered_partial_plist = []
401*6777b538SAndroid Build Coastguard Worker  for plist in partial_plist:
402*6777b538SAndroid Build Coastguard Worker    plist_size = os.stat(plist).st_size
403*6777b538SAndroid Build Coastguard Worker    if plist_size:
404*6777b538SAndroid Build Coastguard Worker      filtered_partial_plist.append(plist)
405*6777b538SAndroid Build Coastguard Worker
406*6777b538SAndroid Build Coastguard Worker  # Invoke the plist_compiler script. It needs to be a python script.
407*6777b538SAndroid Build Coastguard Worker  subprocess.check_call([
408*6777b538SAndroid Build Coastguard Worker      'python3',
409*6777b538SAndroid Build Coastguard Worker      plist_compiler,
410*6777b538SAndroid Build Coastguard Worker      'merge',
411*6777b538SAndroid Build Coastguard Worker      '-f',
412*6777b538SAndroid Build Coastguard Worker      'binary1',
413*6777b538SAndroid Build Coastguard Worker      '-o',
414*6777b538SAndroid Build Coastguard Worker      bundle.info_plist_path,
415*6777b538SAndroid Build Coastguard Worker  ] + filtered_partial_plist)
416*6777b538SAndroid Build Coastguard Worker
417*6777b538SAndroid Build Coastguard Worker
418*6777b538SAndroid Build Coastguard Workerclass Action(object):
419*6777b538SAndroid Build Coastguard Worker  """Class implementing one action supported by the script."""
420*6777b538SAndroid Build Coastguard Worker
421*6777b538SAndroid Build Coastguard Worker  @classmethod
422*6777b538SAndroid Build Coastguard Worker  def Register(cls, subparsers):
423*6777b538SAndroid Build Coastguard Worker    parser = subparsers.add_parser(cls.name, help=cls.help)
424*6777b538SAndroid Build Coastguard Worker    parser.set_defaults(func=cls._Execute)
425*6777b538SAndroid Build Coastguard Worker    cls._Register(parser)
426*6777b538SAndroid Build Coastguard Worker
427*6777b538SAndroid Build Coastguard Worker
428*6777b538SAndroid Build Coastguard Workerclass CodeSignBundleAction(Action):
429*6777b538SAndroid Build Coastguard Worker  """Class implementing the code-sign-bundle action."""
430*6777b538SAndroid Build Coastguard Worker
431*6777b538SAndroid Build Coastguard Worker  name = 'code-sign-bundle'
432*6777b538SAndroid Build Coastguard Worker  help = 'perform code signature for a bundle'
433*6777b538SAndroid Build Coastguard Worker
434*6777b538SAndroid Build Coastguard Worker  @staticmethod
435*6777b538SAndroid Build Coastguard Worker  def _Register(parser):
436*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
437*6777b538SAndroid Build Coastguard Worker        '--entitlements', '-e', dest='entitlements_path',
438*6777b538SAndroid Build Coastguard Worker        help='path to the entitlements file to use')
439*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
440*6777b538SAndroid Build Coastguard Worker        'path', help='path to the iOS bundle to codesign')
441*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
442*6777b538SAndroid Build Coastguard Worker        '--identity', '-i', required=True,
443*6777b538SAndroid Build Coastguard Worker        help='identity to use to codesign')
444*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
445*6777b538SAndroid Build Coastguard Worker        '--binary', '-b', required=True,
446*6777b538SAndroid Build Coastguard Worker        help='path to the iOS bundle binary')
447*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
448*6777b538SAndroid Build Coastguard Worker        '--framework', '-F', action='append', default=[], dest='frameworks',
449*6777b538SAndroid Build Coastguard Worker        help='install and resign system framework')
450*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
451*6777b538SAndroid Build Coastguard Worker        '--disable-code-signature', action='store_true', dest='no_signature',
452*6777b538SAndroid Build Coastguard Worker        help='disable code signature')
453*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
454*6777b538SAndroid Build Coastguard Worker        '--disable-embedded-mobileprovision', action='store_false',
455*6777b538SAndroid Build Coastguard Worker        default=True, dest='embedded_mobileprovision',
456*6777b538SAndroid Build Coastguard Worker        help='disable finding and embedding mobileprovision')
457*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
458*6777b538SAndroid Build Coastguard Worker        '--platform', '-t', required=True,
459*6777b538SAndroid Build Coastguard Worker        help='platform the signed bundle is targeting')
460*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
461*6777b538SAndroid Build Coastguard Worker        '--partial-info-plist', '-p', action='append', default=[],
462*6777b538SAndroid Build Coastguard Worker        help='path to partial Info.plist to merge to create bundle Info.plist')
463*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
464*6777b538SAndroid Build Coastguard Worker        '--plist-compiler-path', '-P', action='store',
465*6777b538SAndroid Build Coastguard Worker        help='path to the plist compiler script (for --partial-info-plist)')
466*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
467*6777b538SAndroid Build Coastguard Worker        '--mobileprovision',
468*6777b538SAndroid Build Coastguard Worker        '-m',
469*6777b538SAndroid Build Coastguard Worker        action='append',
470*6777b538SAndroid Build Coastguard Worker        default=[],
471*6777b538SAndroid Build Coastguard Worker        dest='mobileprovision_files',
472*6777b538SAndroid Build Coastguard Worker        help='list of mobileprovision files to use. If empty, uses the files ' +
473*6777b538SAndroid Build Coastguard Worker        'in $HOME/Library/MobileDevice/Provisioning Profiles')
474*6777b538SAndroid Build Coastguard Worker    parser.set_defaults(no_signature=False)
475*6777b538SAndroid Build Coastguard Worker
476*6777b538SAndroid Build Coastguard Worker  @staticmethod
477*6777b538SAndroid Build Coastguard Worker  def _Execute(args):
478*6777b538SAndroid Build Coastguard Worker    if not args.identity:
479*6777b538SAndroid Build Coastguard Worker      args.identity = '-'
480*6777b538SAndroid Build Coastguard Worker
481*6777b538SAndroid Build Coastguard Worker    bundle = Bundle(args.path, args.platform)
482*6777b538SAndroid Build Coastguard Worker
483*6777b538SAndroid Build Coastguard Worker    if args.partial_info_plist:
484*6777b538SAndroid Build Coastguard Worker      GenerateBundleInfoPlist(bundle, args.plist_compiler_path,
485*6777b538SAndroid Build Coastguard Worker                              args.partial_info_plist)
486*6777b538SAndroid Build Coastguard Worker
487*6777b538SAndroid Build Coastguard Worker    # The bundle Info.plist may have been updated by GenerateBundleInfoPlist()
488*6777b538SAndroid Build Coastguard Worker    # above. Load the bundle information from Info.plist after the modification
489*6777b538SAndroid Build Coastguard Worker    # have been written to disk.
490*6777b538SAndroid Build Coastguard Worker    bundle.Load()
491*6777b538SAndroid Build Coastguard Worker
492*6777b538SAndroid Build Coastguard Worker    # According to Apple documentation, the application binary must be the same
493*6777b538SAndroid Build Coastguard Worker    # as the bundle name without the .app suffix. See crbug.com/740476 for more
494*6777b538SAndroid Build Coastguard Worker    # information on what problem this can cause.
495*6777b538SAndroid Build Coastguard Worker    #
496*6777b538SAndroid Build Coastguard Worker    # To prevent this class of error, fail with an error if the binary name is
497*6777b538SAndroid Build Coastguard Worker    # incorrect in the Info.plist as it is not possible to update the value in
498*6777b538SAndroid Build Coastguard Worker    # Info.plist at this point (the file has been copied by a different target
499*6777b538SAndroid Build Coastguard Worker    # and ninja would consider the build dirty if it was updated).
500*6777b538SAndroid Build Coastguard Worker    #
501*6777b538SAndroid Build Coastguard Worker    # Also checks that the name of the bundle is correct too (does not cause the
502*6777b538SAndroid Build Coastguard Worker    # build to be considered dirty, but still terminate the script in case of an
503*6777b538SAndroid Build Coastguard Worker    # incorrect bundle name).
504*6777b538SAndroid Build Coastguard Worker    #
505*6777b538SAndroid Build Coastguard Worker    # Apple documentation is available at:
506*6777b538SAndroid Build Coastguard Worker    # https://developer.apple.com/library/content/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html
507*6777b538SAndroid Build Coastguard Worker    bundle_name = os.path.splitext(os.path.basename(bundle.path))[0]
508*6777b538SAndroid Build Coastguard Worker    errors = bundle.Validate({
509*6777b538SAndroid Build Coastguard Worker        'CFBundleName': bundle_name,
510*6777b538SAndroid Build Coastguard Worker        'CFBundleExecutable': bundle_name,
511*6777b538SAndroid Build Coastguard Worker    })
512*6777b538SAndroid Build Coastguard Worker    if errors:
513*6777b538SAndroid Build Coastguard Worker      for key in sorted(errors):
514*6777b538SAndroid Build Coastguard Worker        value, expected_value = errors[key]
515*6777b538SAndroid Build Coastguard Worker        sys.stderr.write('%s: error: %s value incorrect: %s != %s\n' % (
516*6777b538SAndroid Build Coastguard Worker            bundle.path, key, value, expected_value))
517*6777b538SAndroid Build Coastguard Worker      sys.stderr.flush()
518*6777b538SAndroid Build Coastguard Worker      sys.exit(1)
519*6777b538SAndroid Build Coastguard Worker
520*6777b538SAndroid Build Coastguard Worker    # Delete existing embedded mobile provisioning.
521*6777b538SAndroid Build Coastguard Worker    embedded_provisioning_profile = os.path.join(
522*6777b538SAndroid Build Coastguard Worker        bundle.path, 'embedded.mobileprovision')
523*6777b538SAndroid Build Coastguard Worker    if os.path.isfile(embedded_provisioning_profile):
524*6777b538SAndroid Build Coastguard Worker      os.unlink(embedded_provisioning_profile)
525*6777b538SAndroid Build Coastguard Worker
526*6777b538SAndroid Build Coastguard Worker    # Delete existing code signature.
527*6777b538SAndroid Build Coastguard Worker    if os.path.exists(bundle.signature_dir):
528*6777b538SAndroid Build Coastguard Worker      shutil.rmtree(bundle.signature_dir)
529*6777b538SAndroid Build Coastguard Worker
530*6777b538SAndroid Build Coastguard Worker    # Install system frameworks if requested.
531*6777b538SAndroid Build Coastguard Worker    for framework_path in args.frameworks:
532*6777b538SAndroid Build Coastguard Worker      InstallSystemFramework(framework_path, args.path, args)
533*6777b538SAndroid Build Coastguard Worker
534*6777b538SAndroid Build Coastguard Worker    # Copy main binary into bundle.
535*6777b538SAndroid Build Coastguard Worker    if not os.path.isdir(bundle.executable_dir):
536*6777b538SAndroid Build Coastguard Worker      os.makedirs(bundle.executable_dir)
537*6777b538SAndroid Build Coastguard Worker    shutil.copy(args.binary, bundle.binary_path)
538*6777b538SAndroid Build Coastguard Worker
539*6777b538SAndroid Build Coastguard Worker    if bundle.kind == 'mac_framework':
540*6777b538SAndroid Build Coastguard Worker      # Create Versions/Current -> Versions/A symlink
541*6777b538SAndroid Build Coastguard Worker      CreateSymlink('A', os.path.join(bundle.path, 'Versions/Current'))
542*6777b538SAndroid Build Coastguard Worker
543*6777b538SAndroid Build Coastguard Worker      # Create $binary_name -> Versions/Current/$binary_name symlink
544*6777b538SAndroid Build Coastguard Worker      CreateSymlink(os.path.join('Versions/Current', bundle.binary_name),
545*6777b538SAndroid Build Coastguard Worker                    os.path.join(bundle.path, bundle.binary_name))
546*6777b538SAndroid Build Coastguard Worker
547*6777b538SAndroid Build Coastguard Worker      # Create optional symlinks.
548*6777b538SAndroid Build Coastguard Worker      for name in ('Headers', 'Resources', 'Modules'):
549*6777b538SAndroid Build Coastguard Worker        target = os.path.join(bundle.path, 'Versions/A', name)
550*6777b538SAndroid Build Coastguard Worker        if os.path.exists(target):
551*6777b538SAndroid Build Coastguard Worker          CreateSymlink(os.path.join('Versions/Current', name),
552*6777b538SAndroid Build Coastguard Worker                        os.path.join(bundle.path, name))
553*6777b538SAndroid Build Coastguard Worker        else:
554*6777b538SAndroid Build Coastguard Worker          obsolete_path = os.path.join(bundle.path, name)
555*6777b538SAndroid Build Coastguard Worker          if os.path.exists(obsolete_path):
556*6777b538SAndroid Build Coastguard Worker            os.unlink(obsolete_path)
557*6777b538SAndroid Build Coastguard Worker
558*6777b538SAndroid Build Coastguard Worker    if args.no_signature:
559*6777b538SAndroid Build Coastguard Worker      return
560*6777b538SAndroid Build Coastguard Worker
561*6777b538SAndroid Build Coastguard Worker    codesign_extra_args = []
562*6777b538SAndroid Build Coastguard Worker
563*6777b538SAndroid Build Coastguard Worker    if args.embedded_mobileprovision:
564*6777b538SAndroid Build Coastguard Worker      # Find mobile provisioning profile and embeds it into the bundle (if a
565*6777b538SAndroid Build Coastguard Worker      # code signing identify has been provided, fails if no valid mobile
566*6777b538SAndroid Build Coastguard Worker      # provisioning is found).
567*6777b538SAndroid Build Coastguard Worker      provisioning_profile_required = args.identity != '-'
568*6777b538SAndroid Build Coastguard Worker      provisioning_profile = FindProvisioningProfile(
569*6777b538SAndroid Build Coastguard Worker          args.mobileprovision_files, bundle.identifier,
570*6777b538SAndroid Build Coastguard Worker          provisioning_profile_required)
571*6777b538SAndroid Build Coastguard Worker      if provisioning_profile and args.platform != 'iphonesimulator':
572*6777b538SAndroid Build Coastguard Worker        provisioning_profile.Install(embedded_provisioning_profile)
573*6777b538SAndroid Build Coastguard Worker
574*6777b538SAndroid Build Coastguard Worker        if args.entitlements_path is not None:
575*6777b538SAndroid Build Coastguard Worker          temporary_entitlements_file = \
576*6777b538SAndroid Build Coastguard Worker              tempfile.NamedTemporaryFile(suffix='.xcent')
577*6777b538SAndroid Build Coastguard Worker          codesign_extra_args.extend(
578*6777b538SAndroid Build Coastguard Worker              ['--entitlements', temporary_entitlements_file.name])
579*6777b538SAndroid Build Coastguard Worker
580*6777b538SAndroid Build Coastguard Worker          entitlements = GenerateEntitlements(
581*6777b538SAndroid Build Coastguard Worker              args.entitlements_path, provisioning_profile, bundle.identifier)
582*6777b538SAndroid Build Coastguard Worker          entitlements.WriteTo(temporary_entitlements_file.name)
583*6777b538SAndroid Build Coastguard Worker
584*6777b538SAndroid Build Coastguard Worker    CodeSignBundle(bundle.path, args.identity, codesign_extra_args)
585*6777b538SAndroid Build Coastguard Worker
586*6777b538SAndroid Build Coastguard Worker
587*6777b538SAndroid Build Coastguard Workerclass CodeSignFileAction(Action):
588*6777b538SAndroid Build Coastguard Worker  """Class implementing code signature for a single file."""
589*6777b538SAndroid Build Coastguard Worker
590*6777b538SAndroid Build Coastguard Worker  name = 'code-sign-file'
591*6777b538SAndroid Build Coastguard Worker  help = 'code-sign a single file'
592*6777b538SAndroid Build Coastguard Worker
593*6777b538SAndroid Build Coastguard Worker  @staticmethod
594*6777b538SAndroid Build Coastguard Worker  def _Register(parser):
595*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
596*6777b538SAndroid Build Coastguard Worker        'path', help='path to the file to codesign')
597*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
598*6777b538SAndroid Build Coastguard Worker        '--identity', '-i', required=True,
599*6777b538SAndroid Build Coastguard Worker        help='identity to use to codesign')
600*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
601*6777b538SAndroid Build Coastguard Worker        '--output', '-o',
602*6777b538SAndroid Build Coastguard Worker        help='if specified copy the file to that location before signing it')
603*6777b538SAndroid Build Coastguard Worker    parser.set_defaults(sign=True)
604*6777b538SAndroid Build Coastguard Worker
605*6777b538SAndroid Build Coastguard Worker  @staticmethod
606*6777b538SAndroid Build Coastguard Worker  def _Execute(args):
607*6777b538SAndroid Build Coastguard Worker    if not args.identity:
608*6777b538SAndroid Build Coastguard Worker      args.identity = '-'
609*6777b538SAndroid Build Coastguard Worker
610*6777b538SAndroid Build Coastguard Worker    install_path = args.path
611*6777b538SAndroid Build Coastguard Worker    if args.output:
612*6777b538SAndroid Build Coastguard Worker
613*6777b538SAndroid Build Coastguard Worker      if os.path.isfile(args.output):
614*6777b538SAndroid Build Coastguard Worker        os.unlink(args.output)
615*6777b538SAndroid Build Coastguard Worker      elif os.path.isdir(args.output):
616*6777b538SAndroid Build Coastguard Worker        shutil.rmtree(args.output)
617*6777b538SAndroid Build Coastguard Worker
618*6777b538SAndroid Build Coastguard Worker      if os.path.isfile(args.path):
619*6777b538SAndroid Build Coastguard Worker        shutil.copy(args.path, args.output)
620*6777b538SAndroid Build Coastguard Worker      elif os.path.isdir(args.path):
621*6777b538SAndroid Build Coastguard Worker        shutil.copytree(args.path, args.output)
622*6777b538SAndroid Build Coastguard Worker
623*6777b538SAndroid Build Coastguard Worker      install_path = args.output
624*6777b538SAndroid Build Coastguard Worker
625*6777b538SAndroid Build Coastguard Worker    CodeSignBundle(install_path, args.identity,
626*6777b538SAndroid Build Coastguard Worker      ['--deep', '--preserve-metadata=identifier,entitlements'])
627*6777b538SAndroid Build Coastguard Worker
628*6777b538SAndroid Build Coastguard Worker
629*6777b538SAndroid Build Coastguard Workerclass GenerateEntitlementsAction(Action):
630*6777b538SAndroid Build Coastguard Worker  """Class implementing the generate-entitlements action."""
631*6777b538SAndroid Build Coastguard Worker
632*6777b538SAndroid Build Coastguard Worker  name = 'generate-entitlements'
633*6777b538SAndroid Build Coastguard Worker  help = 'generate entitlements file'
634*6777b538SAndroid Build Coastguard Worker
635*6777b538SAndroid Build Coastguard Worker  @staticmethod
636*6777b538SAndroid Build Coastguard Worker  def _Register(parser):
637*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
638*6777b538SAndroid Build Coastguard Worker        '--entitlements', '-e', dest='entitlements_path',
639*6777b538SAndroid Build Coastguard Worker        help='path to the entitlements file to use')
640*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
641*6777b538SAndroid Build Coastguard Worker        'path', help='path to the entitlements file to generate')
642*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
643*6777b538SAndroid Build Coastguard Worker        '--info-plist', '-p', required=True,
644*6777b538SAndroid Build Coastguard Worker        help='path to the bundle Info.plist')
645*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
646*6777b538SAndroid Build Coastguard Worker        '--mobileprovision',
647*6777b538SAndroid Build Coastguard Worker        '-m',
648*6777b538SAndroid Build Coastguard Worker        action='append',
649*6777b538SAndroid Build Coastguard Worker        default=[],
650*6777b538SAndroid Build Coastguard Worker        dest='mobileprovision_files',
651*6777b538SAndroid Build Coastguard Worker        help='set of mobileprovision files to use. If empty, uses the files ' +
652*6777b538SAndroid Build Coastguard Worker        'in $HOME/Library/MobileDevice/Provisioning Profiles')
653*6777b538SAndroid Build Coastguard Worker
654*6777b538SAndroid Build Coastguard Worker  @staticmethod
655*6777b538SAndroid Build Coastguard Worker  def _Execute(args):
656*6777b538SAndroid Build Coastguard Worker    info_plist = LoadPlistFile(args.info_plist)
657*6777b538SAndroid Build Coastguard Worker    bundle_identifier = info_plist['CFBundleIdentifier']
658*6777b538SAndroid Build Coastguard Worker    provisioning_profile = FindProvisioningProfile(args.mobileprovision_files,
659*6777b538SAndroid Build Coastguard Worker                                                   bundle_identifier, False)
660*6777b538SAndroid Build Coastguard Worker    entitlements = GenerateEntitlements(
661*6777b538SAndroid Build Coastguard Worker        args.entitlements_path, provisioning_profile, bundle_identifier)
662*6777b538SAndroid Build Coastguard Worker    entitlements.WriteTo(args.path)
663*6777b538SAndroid Build Coastguard Worker
664*6777b538SAndroid Build Coastguard Worker
665*6777b538SAndroid Build Coastguard Workerclass FindProvisioningProfileAction(Action):
666*6777b538SAndroid Build Coastguard Worker  """Class implementing the find-codesign-identity action."""
667*6777b538SAndroid Build Coastguard Worker
668*6777b538SAndroid Build Coastguard Worker  name = 'find-provisioning-profile'
669*6777b538SAndroid Build Coastguard Worker  help = 'find provisioning profile for use by Xcode project generator'
670*6777b538SAndroid Build Coastguard Worker
671*6777b538SAndroid Build Coastguard Worker  @staticmethod
672*6777b538SAndroid Build Coastguard Worker  def _Register(parser):
673*6777b538SAndroid Build Coastguard Worker    parser.add_argument('--bundle-id',
674*6777b538SAndroid Build Coastguard Worker                        '-b',
675*6777b538SAndroid Build Coastguard Worker                        required=True,
676*6777b538SAndroid Build Coastguard Worker                        help='bundle identifier')
677*6777b538SAndroid Build Coastguard Worker    parser.add_argument(
678*6777b538SAndroid Build Coastguard Worker        '--mobileprovision',
679*6777b538SAndroid Build Coastguard Worker        '-m',
680*6777b538SAndroid Build Coastguard Worker        action='append',
681*6777b538SAndroid Build Coastguard Worker        default=[],
682*6777b538SAndroid Build Coastguard Worker        dest='mobileprovision_files',
683*6777b538SAndroid Build Coastguard Worker        help='set of mobileprovision files to use. If empty, uses the files ' +
684*6777b538SAndroid Build Coastguard Worker        'in $HOME/Library/MobileDevice/Provisioning Profiles')
685*6777b538SAndroid Build Coastguard Worker
686*6777b538SAndroid Build Coastguard Worker  @staticmethod
687*6777b538SAndroid Build Coastguard Worker  def _Execute(args):
688*6777b538SAndroid Build Coastguard Worker    provisioning_profile_info = {}
689*6777b538SAndroid Build Coastguard Worker    provisioning_profile = FindProvisioningProfile(args.mobileprovision_files,
690*6777b538SAndroid Build Coastguard Worker                                                   args.bundle_id, False)
691*6777b538SAndroid Build Coastguard Worker    for key in ('team_identifier', 'name'):
692*6777b538SAndroid Build Coastguard Worker      if provisioning_profile:
693*6777b538SAndroid Build Coastguard Worker        provisioning_profile_info[key] = getattr(provisioning_profile, key)
694*6777b538SAndroid Build Coastguard Worker      else:
695*6777b538SAndroid Build Coastguard Worker        provisioning_profile_info[key] = ''
696*6777b538SAndroid Build Coastguard Worker    print(json.dumps(provisioning_profile_info))
697*6777b538SAndroid Build Coastguard Worker
698*6777b538SAndroid Build Coastguard Worker
699*6777b538SAndroid Build Coastguard Workerdef Main():
700*6777b538SAndroid Build Coastguard Worker  # Cache this codec so that plistlib can find it. See
701*6777b538SAndroid Build Coastguard Worker  # https://crbug.com/999461#c12 for more details.
702*6777b538SAndroid Build Coastguard Worker  codecs.lookup('utf-8')
703*6777b538SAndroid Build Coastguard Worker
704*6777b538SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser('codesign iOS bundles')
705*6777b538SAndroid Build Coastguard Worker  subparsers = parser.add_subparsers()
706*6777b538SAndroid Build Coastguard Worker
707*6777b538SAndroid Build Coastguard Worker  actions = [
708*6777b538SAndroid Build Coastguard Worker      CodeSignBundleAction,
709*6777b538SAndroid Build Coastguard Worker      CodeSignFileAction,
710*6777b538SAndroid Build Coastguard Worker      GenerateEntitlementsAction,
711*6777b538SAndroid Build Coastguard Worker      FindProvisioningProfileAction,
712*6777b538SAndroid Build Coastguard Worker  ]
713*6777b538SAndroid Build Coastguard Worker
714*6777b538SAndroid Build Coastguard Worker  for action in actions:
715*6777b538SAndroid Build Coastguard Worker    action.Register(subparsers)
716*6777b538SAndroid Build Coastguard Worker
717*6777b538SAndroid Build Coastguard Worker  args = parser.parse_args()
718*6777b538SAndroid Build Coastguard Worker  args.func(args)
719*6777b538SAndroid Build Coastguard Worker
720*6777b538SAndroid Build Coastguard Worker
721*6777b538SAndroid Build Coastguard Workerif __name__ == '__main__':
722*6777b538SAndroid Build Coastguard Worker  sys.exit(Main())
723