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