def install_profile(profile_path): uuid = provtool.uuid(profile_path) dst_dir = provtool.DEFAULT_PROVPROF_DIR dst_name = "%s.mobileprovision" % (uuid) dst_path = os.path.join(dst_dir, dst_name) shutil.copyfile(profile_path, dst_path) return dst_path
def build_ipa(workspace=None, scheme=None, project=None, target=None, config=None, profile=None, identity=None, keychain=None, keychain_password=None, output=None, overwrite=False, build_dir=None, dsym=False, clean=False, **kwargs): if profile is not None: prov_profile_path = _find_prov_profile(profile) if prov_profile_path is None: # TODO: better error handling print "couldn't find profile" sys.exit(1) if keychain is not None and keychain_password is not None: keychain_cmd = unlock_keychain_cmd( keychain, keychain_password) else: keychain_cmd = None config = config or defaults['build_config'] build_args = ['-sdk', 'iphoneos'] if clean: build_args.extend(['clean']) build_args.extend(['build', '-config', config]) build_args.extend(_determine_target_args(workspace=workspace, scheme=scheme, project=project, target=target)) if identity is not None: build_args.extend([ 'CODE_SIGN_IDENTITY=%s' % (identity) ]) if keychain_cmd is not None: run_cmd(add_keychain_cmd(keychain)) build_args.extend([ 'OTHER_CODE_SIGN_FLAGS=--keychain=%s' % find_keychain(keychain) ]) if build_dir is not None: build_args.extend([ 'SYMROOT=%s' % (os.path.realpath(build_dir)) ]) if profile is not None: build_args.extend([ 'PROVISIONING_PROFILE=%s' % (provtool.uuid(prov_profile_path)) ]) build_settings_cmd = ['xcodebuild', '-showBuildSettings'] + build_args print shellify(build_settings_cmd) build_settings_output = check_output(build_settings_cmd) build_settings = _parse_build_settings(build_settings_output) build_cmd = shellify(['xcodebuild'] + build_args) if keychain_cmd is not None: # unlocking keychain in the same shell to try to prevent # "User Interaction is Not Allowed" errors build_cmd = join_cmds(keychain_cmd, build_cmd) print build_cmd run_cmd(build_cmd) built_products_dir = build_settings['BUILT_PRODUCTS_DIR'] full_product_name = build_settings['FULL_PRODUCT_NAME'] full_product_path = os.path.join(built_products_dir, full_product_name) # read Info.plist info_plist_path = os.path.join(built_products_dir, build_settings['INFOPLIST_PATH']) info_plist = biplist.readPlist(info_plist_path) build_version = info_plist['CFBundleVersion'] marketing_version = info_plist['CFBundleShortVersionString'] # unlock the keychain again if keychain_password is not None: run_cmd(keychain_cmd) package_args = ['xcrun', '-v', '-sdk', 'iphoneos', 'PackageApplication', full_product_path, '--embed', prov_profile_path] if identity is not None: package_args.extend([ '--sign', identity, ]) package_cmd = shellify(package_args) print package_cmd if keychain_cmd is not None: package_cmd = join_cmds(keychain_cmd, package_cmd) check_call(package_cmd, shell=True) # shutil.move has some odd behavior. If you specifiy a full absolute path as # the destination, and the path exists, it will overwrite it. If you # specify a directory as the output path, and a file in that directory # exists, it will fail. We try to preserve that behavior. full_ipa_path = full_product_path[:-3] + 'ipa' output_path = os.path.abspath(output) full_output_path = output_path if os.path.isdir(output_path): app_name = os.path.splitext(full_product_name)[0] ipa_name = '%s_%s_%s_%s.ipa' % (app_name, marketing_version, build_version, config) full_output_path = os.path.join(output_path, ipa_name) if overwrite and os.path.exists(full_output_path): os.remove(full_output_path) shutil.move(full_ipa_path, full_output_path) if dsym: dsym_name = os.path.basename(full_product_path) + '.dSYM' ipa_name = os.path.basename(full_output_path) output_dir = os.path.dirname(full_output_path) dsym_zip_name = os.path.splitext(ipa_name)[0] + '.dSYM.zip' dsym_zip_path = os.path.join(output_dir, dsym_zip_name) run_cmd(shellify(['zip', '-y', '-r', dsym_zip_path, dsym_name]), cwd=built_products_dir)