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