# -*- coding:utf-8 -*- import file_utils, http_utils, contants, text_utils, apk_tool, path_utils, icon_utils, package_utils import json, os, shutil, time, traceback from PrintLog import PrintLog def startMerge(config_json_path): apk_decompile_tmp_dir = '' try: time_start = time.time() json_text = file_utils.read_file(config_json_path) config = json.loads(json_text) gcp_code = config['gcp_code'] http_utils.notify_cut_state(gcp_code, "5", "开始读取配置") PrintLog.LOGFILE = os.path.join(path_utils.get_sdk_log_path(gcp_code), gcp_code) if os.path.exists(PrintLog.LOGFILE): os.remove(PrintLog.LOGFILE) PrintLog("[gcp_code] : %s" % gcp_code) PrintLog("[config_json_path] : %s" % config_json_path) if 'refactorSdk' in config and toBoolean(config['refactorSdk']): PrintLog("using refactorSdk") 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 1 # 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 = "包名包含特殊字符,不允许切包" PrintLog(errorMsg) http_utils.notify_cut_state(gcp_code, "99", errorMsg) return # cid cid = config['cid'] PrintLog("[cid] : %s" % cid) # gid gid = config['gid'] PrintLog("[gid] : %s" % gid) channel_path = path_utils.get_sdk_channel_path(config['channel_key']) PrintLog("[channel_path] : %s" % channel_path) if not os.path.exists(channel_path): errorMsg = "该渠道尚未接入完成" PrintLog(errorMsg) http_utils.notify_cut_state(gcp_code, "99", errorMsg) return # keystore配置文件 keystore_path = config['keystore_path'] PrintLog("[keystore_path] : %s" % keystore_path) if not os.path.exists(keystore_path): errorMsg = "找不到签名文件,请检查" PrintLog(errorMsg) http_utils.notify_cut_state(gcp_code, "99", errorMsg) return 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 != None: PrintLog("母包未更新,无需重新反编译") if ret == 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) # minSDKVersion = meta_config['minSDKVersion'] # PrintLog("[minSDKVersion]: %s"%minSDKVersion) 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, sdk_name, package_name): PrintLog("合并代码失败") return 1 # 修改${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, 'res', 'drawable', 'yyxx_comm_welcome') 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 1 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]) meta_config = config['meta_config'] 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") ret = apk_tool.recompile(apk_decompile_tmp_dir, out_put_unsigned_apk_path) if ret: PrintLog('apk回编译失败') return ret http_utils.notify_cut_state(gcp_code, "90", "开始重签名,对齐APK") ret = apk_tool.signer(out_put_unsigned_apk_path, out_put_signed_apk_path, keystore_path, storepass, alias, keypass) if ret: PrintLog('apk签名失败') return ret ret = apk_tool.zipalign(out_put_signed_apk_path, out_put_zipalign_apk_path) if ret: PrintLog('aapk压缩失败') return ret dis_path = os.path.join(contants.get_out_put_dir(), "g%s_%s_%s_%s.apk" % (gid, sdk_name, package_name, gcp_code)) if not os.path.exists(contants.get_out_put_dir()): os.makedirs(contants.get_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)) except: PrintLog(traceback.format_exc()) finally: 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)) def toBoolean(bool_str): if type(bool_str) == bool: return bool_str if bool_str == 'true': return True return False