# -*- coding:utf-8 -*- import icon_utils, file_utils, package_utils, apk_tool, path_utils, contants, http_utils, print_log, text_utils,xml_utils import json, os, shutil, time, traceback from print_log import printlog def startMerge(config_json_path): global gcp_code errorMsg = "切包失败,请联系开发人员" apk_decompile_tmp_dir = '' isSuccess = False try: time_start = time.time() json_text = file_utils.read_file(config_json_path) config = json.loads(json_text) gcp_code = config['gcp_code'] print_log.LOGFILE = os.path.join(contants.sdk_log_path, "%s.txt" % gcp_code) if os.path.exists(print_log.LOGFILE): os.remove(print_log.LOGFILE) printlog("[LOGFILE] : %s" % print_log.LOGFILE) http_utils.notify_cut_state(gcp_code, "5", "开始读取配置") printlog("[gcp_code] : %s" % gcp_code) printlog("[config_json_path] : %s" % config_json_path) origin_apk_full_path = config['apk_path'] printlog("[origin_apk_full_path] : %s" % origin_apk_full_path) if not os.path.exists(origin_apk_full_path): errorMsg = "找不到游戏母包,请检查" printlog(errorMsg) http_utils.notify_cut_state(gcp_code, "99", errorMsg) return isSuccess # apk文件全名(eg: 360_0.01.150608_high_all.apk ) origin_apk_full_name = os.path.basename(origin_apk_full_path) printlog("[origin_apk_full_name] : %s" % origin_apk_full_name) # apk文件名(eg: 360_0.01.150608_high_all) origin_apk_name = os.path.splitext(origin_apk_full_name)[0] printlog("[origin_apk_name] : %s" % origin_apk_name) # apk文件路径(eg: /sdk/develop/tools/outputs/mxd/package/0.01.150608/360 ) origin_apk_dir = os.path.dirname(origin_apk_full_path) printlog("[origin_apk_dir] : %s" % origin_apk_dir) # 渠道名 sdk_name = config['channel_key'] # sys.argv[2] printlog("[sdk_name] : %s" % sdk_name) # 包名 package_name = config['package_name'] printlog("[package_name] : %s" % package_name) result = text_utils.isMatchRegExp(package_name, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.") if result is False: errorMsg = "包名包含特殊字符,不允许切包" return isSuccess # cid cid = config['cid'] printlog("[cid] : %s" % cid) # gid gid = config['gid'] printlog("[gid] : %s" % gid) # 使用主体 platform = config['meta_config'].get('platform','hnyy') platform_path = path_utils.get_full_path('platform_sdk',platform) printlog("[platform] : %s" % platform) channel_path = os.path.join(path_utils.get_sdk_channel_path(config['channel_key']), 'pretreatment_project') printlog("[channel_path] : %s" % channel_path) if not os.path.exists(channel_path): errorMsg = "该渠道尚未接入完成" return isSuccess # keystore配置文件 keystore_path = config['keystore_path'] printlog("[keystore_path] : %s" % keystore_path) if not os.path.exists(keystore_path): errorMsg = "找不到签名文件,请检查" return isSuccess storepass = config['storepass'] printlog("[storepass] : %s" % storepass) keypass = config['keypass'] printlog("[keypass] : %s" % keypass) alias = config['alias'] printlog("[alias] : %s" % alias) # 反编译原apk文件的输出路径 apk_decompile_out_dir = os.path.join(origin_apk_dir, origin_apk_name) printlog("[apk_decompile_out_dir] : %s" % apk_decompile_out_dir) # icon路径 icon_path = config['icon'] printlog("[icon_path]: %s" % icon_path) http_utils.notify_cut_state(gcp_code, "10", "开始反编译文件") # 判断反编译出的文件修改时间与apk修改时间比较,若比apk最则重新反编译 ret = file_utils.compareFileModifyTime(origin_apk_full_path, apk_decompile_out_dir) if ret is not None: printlog("母包未更新,无需重新反编译") if ret is None or ret > 0: # 反编译原apk文件 apk_tool.decompile(origin_apk_full_path, apk_decompile_out_dir) meta_config = config['meta_config'] # 版本号 version = meta_config['version'] printlog("[version]: %s" % version) v_code = meta_config['v_code'] printlog("[v_code]: %s" % v_code) targetSdkVersion = meta_config['targetSdkVersion'] printlog("[targetSdkVersion]: %s" % targetSdkVersion) orientation = meta_config['SDK_ORIENTATION'] printlog("[orientation]: %s" % orientation) http_utils.notify_cut_state(gcp_code, "20", "开始复制文件") randomNumber = text_utils.getRamdomNumber(6) apk_decompile_tmp_dir = os.path.join(origin_apk_dir, randomNumber, "dcm_tmp" + sdk_name) printlog("[apk_decompile_tmp_dir]: %s" % apk_decompile_tmp_dir) # 创建临时目录() printlog("创建并复制母包资源至临时目录") if os.path.exists(apk_decompile_tmp_dir): file_utils.del_file(apk_decompile_tmp_dir) shutil.copytree(apk_decompile_out_dir, apk_decompile_tmp_dir) http_utils.notify_cut_state(gcp_code, "30", "开始合并资源") file_utils.modifyPkgName(apk_decompile_tmp_dir, package_name) if icon_path: icon_utils.replace_icon(apk_decompile_tmp_dir, icon_path) if package_utils.pack(apk_decompile_tmp_dir, channel_path, package_name,config): errorMsg = '合并代码失败' return isSuccess # 修改${applicationId}为包名 splash_path = config['splash'] if splash_path: if os.path.exists(splash_path): apk_temp_splash_path = os.path.join(apk_decompile_tmp_dir, 'assets', 'yyxx_comm_welcome.jpg') shutil.copy(splash_path, apk_temp_splash_path) printlog("复制闪屏页至[apk_temp_splash_path] : %s" % apk_temp_splash_path) else: printlog("找不到闪屏文件,请检查[splash_path] : %s" % splash_path) http_utils.notify_cut_state(gcp_code, "50", "开始替换渠道参数") temp_yyxx_cfg_path = os.path.join(apk_decompile_tmp_dir, 'assets', 'yyxx_game', 'yyxx_cfg.properties') if not os.path.exists(temp_yyxx_cfg_path): printlog('找不到yyxx_cfg.properties,请检查游戏是否正确接入SDK') return isSuccess file_utils.replace_assets_param(temp_yyxx_cfg_path, 'YYXX_GCP_CODE', gcp_code) temp_manifest_path = os.path.join(apk_decompile_tmp_dir, 'AndroidManifest.xml') sdk_client_config = config['sdk_client_config'] printlog('[sdk_client_config] : %s' % sdk_client_config) if sdk_client_config: keys = sdk_client_config.keys() for key in keys: # 替换AndroidManifest.xml中的meta-data字段 file_utils.replace_manifest_meta_data(temp_manifest_path, key, sdk_client_config[key]) # 替换assets/YyrhParam.cnf中的关键字 file_utils.replace_assets_param(temp_yyxx_cfg_path, key, sdk_client_config[key]) # 特殊渠道需要特殊处理 file_utils.special_replace(sdk_name, apk_decompile_tmp_dir, key, sdk_client_config[key]) #可选择不同主体打包,根据不同主体,替换对应资源 if not contants.isTestEnvironment(): if platform == 'hnyy': file_utils.replace_assets_param(temp_yyxx_cfg_path, 'YYXX_ONLINE_ENV', 'https://sdkapi.yyxxgame.com') file_utils.replace_assets_param(temp_yyxx_cfg_path, 'YYXX_PLATFORM', 'HNYY') elif platform == 'hnqj': file_utils.replace_assets_param(temp_yyxx_cfg_path, 'YYXX_ONLINE_ENV', 'https://fxsy.qijinghao.com') file_utils.replace_assets_param(temp_yyxx_cfg_path, 'YYXX_PLATFORM', 'HNQJ') elif platform == 'shxy': file_utils.replace_assets_param(temp_yyxx_cfg_path, 'YYXX_ONLINE_ENV', 'https://sdkapi.bklinok.com') file_utils.replace_assets_param(temp_yyxx_cfg_path, 'YYXX_PLATFORM', 'SHXY') elif platform == 'xinrui': file_utils.replace_assets_param(temp_yyxx_cfg_path, 'YYXX_ONLINE_ENV', 'https://sdkapi.ykklayo.com') file_utils.replace_assets_param(temp_yyxx_cfg_path, 'YYXX_PLATFORM', 'HNXR') else: file_utils.replace_assets_param(temp_yyxx_cfg_path, 'YYXX_ONLINE_ENV', 'http://testsdkapi.yyxxgame.com') if platform != 'hnyy': temp_res_path = os.path.join(apk_decompile_tmp_dir, 'res') file_utils.copyAllFile(platform_path, temp_res_path) if sdk_name == "shxy" or sdk_name == "hnxr" or sdk_name == "hnqj": temp_res_path = os.path.join(apk_decompile_tmp_dir, 'res') sdk_name_path = path_utils.get_full_path('platform_sdk', sdk_name) res_list = [] res_list = xml_utils.read_all_res(os.path.join(sdk_name_path, 'values','yyxx_ui_colors.xml'), res_list) if len(res_list) != 0: xml_utils.remove_same_values_res(os.path.join(temp_res_path, 'values','values.xml'), res_list) package_utils.merge_darwable_res_end_with_v4(apk_decompile_tmp_dir) file_utils.copyAllFile(sdk_name_path, temp_res_path) if meta_config: keys = meta_config.keys() for key in keys: if key == "APP_NAME": file_utils.replace_string_app_name(apk_decompile_tmp_dir, meta_config[key]) elif key == "version": version = meta_config[key] elif key == "v_code": v_code = meta_config[key] elif key == "targetSdkVersion": targetSdkVersion = meta_config[key] # 替换方向 elif key == "SDK_ORIENTATION": file_utils.replace_content(temp_manifest_path, 'sdk_orientation', meta_config[key]) file_utils.replace_assets_param(temp_yyxx_cfg_path, "SDK_ORIENTATION", meta_config[key]) http_utils.notify_cut_state(gcp_code, "60", "开始修改版本号") yml = os.path.join(apk_decompile_tmp_dir, 'apktool.yml') file_utils.changeVersion(yml, v_code, version, targetSdkVersion) file_utils.merge_wx_page(package_name, apk_decompile_tmp_dir) http_utils.notify_cut_state(gcp_code, "70", "开始替换游戏资源") game_resource_replace = config['game_resource_replace'] printlog('[game_resource_replace]:%s' % game_resource_replace) if game_resource_replace: for resource in game_resource_replace: if os.path.exists(resource[1]): file_utils.copy_file(resource[1], os.path.join(apk_decompile_tmp_dir, resource[0]), False) out_put_unsigned_apk_path = os.path.join(apk_decompile_tmp_dir, 'gen', '_unsigned.apk') out_put_signed_apk_path = os.path.join(apk_decompile_tmp_dir, 'gen', '_signed.apk') out_put_zipalign_apk_path = os.path.join(apk_decompile_tmp_dir, 'gen', '_zipaligned.apk') # 再编译 http_utils.notify_cut_state(gcp_code, "80", "正在回编译APK") if apk_tool.recompile(apk_decompile_tmp_dir, out_put_unsigned_apk_path): errorMsg = 'apk回编译失败' return isSuccess http_utils.notify_cut_state(gcp_code, "90", "开始重签名,对齐APK") if apk_tool.V1signer(out_put_unsigned_apk_path, out_put_signed_apk_path, keystore_path, storepass, alias, keypass): errorMsg = 'apk签名失败' return isSuccess printlog("开始执行apk压缩") if apk_tool.zipalign(out_put_signed_apk_path, out_put_zipalign_apk_path): errorMsg = 'apk压缩失败' return isSuccess dis_path = os.path.join(contants.out_put_dir, "g%s_%s_%s_%s.apk" % (gid, sdk_name, package_name, gcp_code)) if not os.path.exists(contants.out_put_dir): os.makedirs(contants.out_put_dir) file_utils.copy_file(out_put_zipalign_apk_path, dis_path) http_utils.notify_cut_state(gcp_code, "100", "切包成功(点击复制链接)") time_c = time.time() - time_start printlog("APK路径:%s\n切包总用时:%s秒" % (dis_path, time_c)) isSuccess = True except: printlog(traceback.format_exc()) finally: if isSuccess: http_utils.notify_cut_state(gcp_code, "100", "切包成功(点击复制链接)") else: http_utils.notify_cut_state(gcp_code, "99", errorMsg) if apk_decompile_tmp_dir: printlog('清空打包临时目录') printlog('[apk_decompile_tmp_dir]:%s' % os.path.dirname(apk_decompile_tmp_dir)) file_utils.delete_folder(os.path.dirname(apk_decompile_tmp_dir)) return isSuccess