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