# -*- coding:utf-8 -*- import os import platform import shutil import xml.etree.ElementTree as ET import traceback import yaml import random from V1 import Contants, Exec_Cmd_Utils, HttpUtils, Replace_GameRes_Utils, PrintLog, CommonUtils, Json_Util, File, \ Replace, apk_utils, Icon_Util from V1.PrintLog import printlog import time SUFFIX_BAT = "" SUFFIX_EXE = "" DIR_SPLIT = "/" # Windows下使用.bat,unix无后缀 if platform.system() == "Windows": SUFFIX_BAT = ".bat" SUFFIX_EXE = ".exe" DIR_SPLIT = "\\" # decompile apk def decompile(apkPath, desPath): printlog("start to apkPath -- %s" % apkPath) global SUFFIX_BAT if os.path.exists(apkPath) == False: printlog("no such apk:%s" % apkPath) return 1 home = apk_utils.getComplieToolsHome() decompileCmd = "%s/apktool%s d -f -o %s/ %s" % (home, SUFFIX_BAT, desPath, apkPath) printlog("decompileCmd:%s" % decompileCmd) ret = os.system(decompileCmd) printlog("finished decompiling -------------------------") return ret # recompile fileset to apk def recompile(apkPath, outputApkPath): global SUFFIX_BAT printlog("------------------------------ start to recompile ------------------------------ ") global SUFFIX_BAT recompileCmd = "%s/apktool%s -q b %s -o %s" % (apk_utils.getComplieToolsHome(), SUFFIX_BAT, apkPath, outputApkPath) status, output = Exec_Cmd_Utils.exeCommonCmd(recompileCmd) printlog(output) if status != 0: return 1 printlog("------------------------------ finished recompiling ------------------------------ ") return 0 def mergeYml(fromfile, tofile): if not os.path.exists(fromfile): return False, "origin yaml file is not exists" if not os.path.exists(tofile): return False, "destination yaml file is not exists" header = "!!brut.androlib.meta.MetaInfo\n" fromcontent = open(fromfile, "r").read() fromcontent = fromcontent.replace(header, "") tocontent = open(tofile, "r").read() tocontent = tocontent.replace(header, "") fromroot = yaml.safe_load(fromcontent) toroot = yaml.safe_load(tocontent) if fromroot == None: printlog("from root is empty") return False, "orgin yaml file cant not be parsed or empty" if toroot == None: printlog("to root is empty") return False, "destination yaml file cant not be parsed or is empty" donotcompresskey = "doNotCompress" if donotcompresskey in fromroot: arr = fromroot[donotcompresskey] if not donotcompresskey in toroot: toroot[donotcompresskey] = arr else: toarr = toroot[donotcompresskey] toroot[donotcompresskey] = list(set(toarr + arr)) unknownfileskey = "unknownFiles" if unknownfileskey in fromroot: fromunknownfilesarr = fromroot[unknownfileskey] else: fromunknownfilesarr = {} tounknownfilesarr = toroot[unknownfileskey] toroot[unknownfileskey] = dict(fromunknownfilesarr.items()).update(tounknownfilesarr.items()) desfile = open(tofile, "w") desfile.writelines(header) yaml.safe_dump(toroot, desfile, default_flow_style=False) return True, "" pass # sdk resources + apk resources def mergeResources(sdkPath, apkPath): printlog("--------------------------- start to merge resources ---------------------------") ret = 0 # merge res # sdk_root_dir = apk_utils.getCutSdkRootHome() + "/" + os.path.basename(sdkPath) ret = ret | File.copyFiles("%s/res" % sdkPath, "%s/res" % apkPath) # copy lib and assets # 避免由于架构导致复制无用架构 ret = ret | mergeLibs(sdkPath, apkPath) ret = ret | File.copyFiles("%s/assets" % sdkPath, "%s/assets" % apkPath) ret = ret | File.mergeDir("%s/smali" % sdkPath, "%s/smali" % apkPath) ret = ret | File.mergeDir("%s/smali_classes2" % sdkPath, "%s/smali_classes2" % apkPath) ret = ret | File.mergeDir("%s/smali_classes3" % sdkPath, "%s/smali_classes3" % apkPath) ret = ret | File.mergeDir("%s/smali_classes4" % sdkPath, "%s/smali_classes4" % apkPath) ret = ret | File.mergeDir("%s/smali_classes5" % sdkPath, "%s/smali_classes5" % apkPath) ret = ret | File.mergeDir("%s/smali_classes6" % sdkPath, "%s/smali_classes6" % apkPath) ret = ret | File.mergeDir("%s/unknown" % sdkPath, "%s/unknown" % apkPath) # 暂时的方案是用sdk的apktool.yml覆盖原包apktool.yml 后期需要做apktool.yml合并 if os.path.exists("%s/apktool.yml" % sdkPath): mergeYml("%s/apktool.yml" % sdkPath, "%s/apktool.yml" % apkPath) # shutil.copy("%s/apktool.yml"%sdkPath, "%s/apktool.yml"%apkPath) # 修改微信支付 # ret = ret | File.copyFiles("%s/smali"%sdkPath,"%s/smali"%apkPath) printlog("--------------------------- finished merging resources ---------------------------") return ret # add 合并lib文件 def mergeLibs(sdkPath, apkPath): printlog("--------------------------- start to merge lib --------------------------------") apkLib = "%s/lib" % apkPath sdkLib = "%s/lib" % sdkPath ret = 0 if not os.path.exists(apkLib) and os.path.exists(sdkLib): shutil.copytree(sdkLib, apkLib) printlog("finished merging lib --------------------------------") return ret apkFiles = os.listdir(apkLib) sdkFiles = os.listdir(sdkLib) for cpFile in sdkFiles: if os.path.isfile(cpFile) == False and cpFile not in apkFiles: continue ret = ret | File.copyFiles("%s/lib/%s" % (sdkPath, cpFile), "%s/lib/%s" % (apkPath, cpFile)) printlog("--------------------------- finished merging lib --------------------------------") return ret # 修改微信支付 def mergeWXPay(sdkPath, apkPath): printlog("--------------------------- start merge wechat pay ---------------------------") sdkAM = "%s/AndroidManifest.xml" % sdkPath sdkAMRoot = ET.parse(sdkAM).getroot() sdkPkgName = sdkAMRoot.attrib["package"].replace(".", "/") sdkWxApiFilePath = "%s/smali/%s/wxapi" % (sdkPath, sdkPkgName) printlog("wx api file path:%s" % sdkWxApiFilePath) if os.path.exists(sdkWxApiFilePath): printlog("begin copy wxapi file") apkWxApi = ET.parse("%s/AndroidManifest.xml" % apkPath).getroot().attrib["package"].replace(".", "/") apkWxApiFilePath = "%s/smali/%s/wxapi" % (apkPath, apkWxApi) printlog("wxapi path:%s" % apkWxApiFilePath) if not os.path.exists(apkWxApiFilePath): os.makedirs(apkWxApiFilePath) # shutil.copy("%s/*"%wxApiFilePath, apkWxApiFilePath) os.system("/bin/cp -r %s/* %s" % (sdkWxApiFilePath, apkWxApiFilePath)) for parent, dirnames, filenames in os.walk(apkWxApiFilePath): printlog("replace pgk ---------") for filename in filenames: printlog("start to replace: %s" % filename) formatFname = "'%s'" % filename replacePkgShell = 'sed -i "s#%s#%s#g" %s/%s' % (sdkPkgName, apkWxApi, apkWxApiFilePath, formatFname) printlog("replace package name shell: %s" % replacePkgShell) os.system(replacePkgShell) printlog("end copy wxapi file -------------------------------") pass # sdkConfig.xml + AndroidManifest.xml # sdkConfigPath: 渠道配置路径 # apkPath:解包后AndroidManifest.xml路径 def mergeManifest(sdkConfigPath, apkPath): printlog("start to merge AndroidManifest.xml -------------------------") ret = 0 ret = ret | File.mergeManifest("%s/AndroidManifest.xml" % apkPath, sdkConfigPath) # 根据配置修改游戏名,包名等 printlog("--------------------------- finished merging AndroidManifest.xml -------------------------") return ret def replacePkgName(manifestPath, pkgName): if not os.path.exists(manifestPath): return False, "manifest path is not exists" if pkgName == None or pkgName == "": return False, "package name is empty" pkgName = pkgName.strip() root = ET.parse(manifestPath).getroot() if root == None: return False, "can's parse manifest" oldpkgname = root.attrib["package"] lines = open(manifestPath, "r").readlines() f = open(manifestPath, "w") for line in lines: line = line.replace(oldpkgname, pkgName) f.write(line) f.close() return True, "" pass # @Description -- 对apk进行重新签名.若未提供对齐后的路径或名字,则以当前的命令在.apk前添加_signed # unsignedApkPath: 未签名的包 # keyConfig: 签名配置(暂时未使用) # outputPath: 签名完成后的包路径 def resignApk(unsignedApkPath, keystorepath, storepass, alias, keypass, outputPath=""): printlog("start to resign apk -----------------------") if outputPath == "": point = unsignedApkPath.rfind(".") outputPath = unsignedApkPath[0:point] + "_signed" + unsignedApkPath[point:] printlog("signedName:%s" % outputPath) # 获取签名算法 getKeystoreAlgorithmShell = "%s/keytool -list -v -keystore %s -alias %s -storepass %s | /bin/sed -n \"13p\" | /usr/bin/awk -F ': ' '{print $2}'" % ( apk_utils.getJavaBinPath(), keystorepath, alias, storepass) ret, algorithm = Exec_Cmd_Utils.exeCommonCmd(getKeystoreAlgorithmShell) algorithmMethod = algorithm.strip() printlog("algorithmMethod: %s" % algorithmMethod) resignCmd = "%s/jarsigner -sigalg %s -digestalg SHA1 -storepass %s -keypass %s -keystore %s -signedjar %s %s %s" % ( apk_utils.getJavaBinPath(), algorithmMethod, storepass, keypass, keystorepath, outputPath, unsignedApkPath, alias) status, output = Exec_Cmd_Utils.exeCommonCmd(resignCmd) if status != 0: return 1 printlog("--------------------------- finished resigning apk --------------------------") return 0 # @Description -- 对apk进行V2重新签名.若未提供对齐后的路径或名字,则以当前的命令在.apk前添加_signed # unsignedApkPath: 未签名已对齐的包 # keyConfig: 签名配置(暂时未使用) # outputPath: 签名完成后的包路径 def resignV2Apk(unsignedApkPath, keystorepath, storepass, alias, keypass, outputPath=""): printlog("--------------------------- start to resign apk by V2 -----------------------") if outputPath == "": point = unsignedApkPath.rfind(".") outputPath = unsignedApkPath[0:point] + "_signed" + unsignedApkPath[point:] printlog("signedName:%s" % outputPath) resignCmd = "/usr/bin/java -jar %s sign --ks %s --ks-key-alias %s --ks-pass pass:%s --key-pass pass:%s --out %s %s" % ( apk_utils.getSignV2Jar(), keystorepath, alias, storepass, keypass, outputPath, unsignedApkPath) printlog(resignCmd) status, output = Exec_Cmd_Utils.exeCommonCmd(resignCmd) if status != 0: return 1 printlog("--------------------------- finished resigning V2 apk --------------------------") return 0 # @Description -- 把重新签名的包做4字节对齐,若未提供对齐后的路径或名字,则以当前的命令在.apk前添加_zipaligned # unZipalignApk: 未对齐的apk路径 # outputPath: 对齐后的文件名 # @return 0为成功 1为失败 def zipalignApk(unZipalignApk, outputPath=""): if outputPath == "": point = unZipalignApk.rfind(".") outputPath = unZipalignApk[0:point] + "_zipaligned" + unZipalignApk[point:] printlog("outputPath:%s" % outputPath) zipalignCmd = "%s/zipalign -f 4 %s %s" % (apk_utils.getSdkToolsPath(), unZipalignApk, outputPath) printlog("zipalignCmd:%s" % zipalignCmd) status, output = Exec_Cmd_Utils.exeCommonCmd(zipalignCmd) printlog(output) if status != 0: return 1 printlog("--------------------------- finished zipaligning apk -----------------------------") return 0 def getRamdomNumber(size): str = "" for i in range(size): ch = chr(random.randrange(ord('0'), ord('9') + 1)) str += ch return str def modifyPkgName(apkPath, pkgName): # 修改包名 manifestRoot = ET.parse("%s/AndroidManifest.xml" % apkPath).getroot() if manifestRoot == None: return 1 if pkgName == "": pkgName = manifestRoot.attrib["package"] printlog("get pkg name:%s ----------------------------" % pkgName) else: oldPkgName = manifestRoot.attrib["package"] content = open("%s/AndroidManifest.xml" % apkPath, "r").read() content = content.replace(oldPkgName, pkgName) open("%s/AndroidManifest.xml" % apkPath, "w").write(content) printlog("old pkg name:%s ----------------------------" % oldPkgName) printlog("new pkg name:%s ----------------------------" % pkgName) def getOldPkgName(apkPath): # 修改包名 manifestRoot = ET.parse("%s/AndroidManifest.xml" % apkPath).getroot() if manifestRoot == None: return "" else: oldPkgName = manifestRoot.attrib["package"] return oldPkgName def startMerge(config_json_path): global gcp_code isSuccess = True errorMsg = "切包失败,请联系开发人员" apk_decompile_tmp_dir = "" try: # 当前脚本路径 # PrintLog "[current_py_path]: %s"%sys.argv[0] # py_dir = sys.path[0] # PrintLog "[py_dir] : %s"%py_dir # 更新svn库,1脚本文件;2sdk内容;3sdk_config;4tools;5doc etc # os.system("svn up " + py_dir + DIR_SPLIT + ".." + DIR_SPLIT) # 第一个参数json文件路径 # config_json_path = sys.argv[1] # 包编号 time_start = time.time() gcp_code = Json_Util.ReadJson(config_json_path, "gcp_code") PrintLog.LOGFILE = "%s/%s.txt" % (Contants.SDK_LOG, gcp_code) if os.path.exists(PrintLog.LOGFILE): os.remove(PrintLog.LOGFILE) HttpUtils.notify_cut_state(gcp_code, "5", "正在读取配置") printlog("[LOGFILE] : %s" % PrintLog.LOGFILE) printlog("[gcp_code] : %s" % gcp_code) printlog("[config_json_path] : %s" % config_json_path) # apk文件的全路径(eg: /sdk/develop/tools/outputs/mxd/package/0.01.150608/360/360_0.01.150608_high_all.apk ) origin_apk_full_path = Json_Util.ReadJson(config_json_path, "apk_path") # sys.argv[1] printlog("[origin_apk_full_path] : %s" % origin_apk_full_path) if not os.path.exists(origin_apk_full_path): errorMsg = "找不到游戏母包,请检查" printlog(errorMsg) HttpUtils.notify_cut_state(gcp_code, "99", errorMsg) return # 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 = Json_Util.ReadJson(config_json_path, "channel_key") # sys.argv[2] printlog("[sdk_name] : %s" % sdk_name) # 包名 package_name = Json_Util.ReadJson(config_json_path, "package_name") # sys.argv[3] printlog("[package_name] : %s" % package_name) result = CommonUtils.isMatchRegExp(package_name, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.") if result == False: errorMsg = "包名包含特殊字符,不允许切包" printlog(errorMsg) HttpUtils.notify_cut_state(gcp_code, "99", errorMsg) return # 游戏编号 g_id = Json_Util.ReadJson(config_json_path, "gid") printlog("[gid] : %s" % g_id) # 游戏c_id c_id = Json_Util.ReadJson(config_json_path, "cid") printlog("[cid] : %s" % c_id) # 渠道文件的根目录 sdk_root_dir = apk_utils.getCutSdkRootHome() printlog("[sdk_root_dir] : %s" % sdk_root_dir) # 打包渠道SDK的目录 channel_sdk_root_dir = sdk_root_dir + DIR_SPLIT + sdk_name printlog("[channel_sdk_root_dir] : %s" % channel_sdk_root_dir) if not os.path.exists(channel_sdk_root_dir): errorMsg = "该渠道尚未接入完成" printlog(errorMsg) HttpUtils.notify_cut_state(gcp_code, "99", errorMsg) return # keystore配置文件 keystorepath = Json_Util.ReadJson(config_json_path, "keystore_path") printlog("[keystorepath] : %s" % keystorepath) if not os.path.exists(keystorepath): errorMsg = "找不到签名文件,请检查" printlog(errorMsg) HttpUtils.notify_cut_state(gcp_code, "99", errorMsg) return storepass = Json_Util.ReadJson(config_json_path, "storepass") printlog("[storepass] : %s" % storepass) keypass = Json_Util.ReadJson(config_json_path, "keypass") printlog("[keypass] : %s" % keypass) alias = Json_Util.ReadJson(config_json_path, "alias") printlog("[alias] : %s" % alias) # 反编译原apk文件的输出路径 apk_decompile_out_dir = origin_apk_dir + DIR_SPLIT + origin_apk_name printlog("[apk_decompile_out_dir] : %s" % apk_decompile_out_dir) # icon路径 icon_path = Json_Util.ReadJson(config_json_path, "icon") printlog("[icon_path]: %s" % icon_path) # 版本号 version = Json_Util.ReadJson(config_json_path, "version") vCode = Json_Util.ReadJson(config_json_path, "v_code") # targetSdkVersion targetSdkVersion = Json_Util.ReadJson(config_json_path, "targetSdkVersion") HttpUtils.notify_cut_state(gcp_code, "10", "正在反编译文件") # 判断反编译出的文件修改时间与apk修改时间比较,若比apk最则重新反编译 ret = File.compareFileModifyTime(origin_apk_full_path, apk_decompile_out_dir) if ret != None: printlog("母包未更新,无需重新反编译") if ret == None or ret > 0: # 反编译原apk文件 decompile(origin_apk_full_path, apk_decompile_out_dir) HttpUtils.notify_cut_state(gcp_code, "20", "正在复制文件") randomNumber = getRamdomNumber(6) apk_decompile_tmp_dir = origin_apk_dir + DIR_SPLIT + randomNumber + DIR_SPLIT + "dcm_tmp" + sdk_name printlog("[apk_decompile_tmp_dir] : %s" % apk_decompile_tmp_dir) shutil.copytree(apk_decompile_out_dir, apk_decompile_tmp_dir) HttpUtils.notify_cut_state(gcp_code, "30", "正在合并资源") # compileResources(apk_decompile_tmp_dir, tmp_dir, package_name) if package_name != "" and package_name != None: printlog("modifyPkgName %s" % package_name) modifyPkgName(apk_decompile_tmp_dir, package_name) pass #添加打点SDK printlog("-----开始添加打点SDK -----") Replace.add_point_smali(sdk_root_dir + DIR_SPLIT + "data-point"+ DIR_SPLIT + "data-point",apk_decompile_tmp_dir) Replace.replaceAssets_Param(apk_decompile_tmp_dir + DIR_SPLIT + "assets" + DIR_SPLIT + "datapoint_config.properties", "DP_BIZ_GCP_CODE", gcp_code) prj_am_path = apk_decompile_tmp_dir + DIR_SPLIT + "AndroidManifest.xml" metadatas = ET.parse(prj_am_path).getroot().find("application").findall("meta-data") for meta in metadatas: if meta.attrib["{http://schemas.android.com/apk/res/android}name"] == "yyrh_game_code": values = meta.attrib["{http://schemas.android.com/apk/res/android}value"] Replace.replaceAssets_Param( apk_decompile_tmp_dir + DIR_SPLIT + "assets" + DIR_SPLIT + "datapoint_config.properties", "DP_BIZ_GAME_CODE", values) printlog("-----打点SDK添加完成 -----") # 合并Manifest文件 mergeManifest(sdk_root_dir + DIR_SPLIT + sdk_name + DIR_SPLIT + sdk_name + "_sdk_config.xml", apk_decompile_tmp_dir) # 合并资源 mergeResources(sdk_root_dir + DIR_SPLIT + sdk_name + DIR_SPLIT + sdk_name, apk_decompile_tmp_dir) # 生成R文件 # Aapt_Util.createRFile(apk_decompile_tmp_dir,True, package_name) # 替换YyrhParam中的gcp_code printlog(apk_decompile_tmp_dir + DIR_SPLIT + "assets" + DIR_SPLIT + "YyrhParam.cnf") Replace.replaceAssets_Param(apk_decompile_tmp_dir + DIR_SPLIT + "assets" + DIR_SPLIT + "YyrhParam.cnf", "GCP_CODE", gcp_code) # 替换Androidmanifest.xml中的yyrh_gameChannelID为gcp_code yyrh_game_channel_id Replace.replaceAM_Meta_data(apk_decompile_tmp_dir + DIR_SPLIT + "AndroidManifest.xml", "yyrh_gameChannelID", gcp_code) # 修改debuggable为false # Replace.replaceAM_Debuggable("%s/AndroidManifest.xml"%apk_decompile_out_dir) # 替换Icon HttpUtils.notify_cut_state(gcp_code, "40", "正在修改资源") # 替换Icon printlog("----- begin replace resource -----") if icon_path: ret = Icon_Util.replaceIcon(icon_path, apk_decompile_tmp_dir) if not ret: errorMsg = "找不到icon,请检查" printlog(errorMsg) HttpUtils.notify_cut_state(gcp_code, "99", errorMsg) # 替换启动页 splash_path = Json_Util.ReadJson(config_json_path, "splash") if splash_path: if os.path.exists(splash_path): shutil.copy(splash_path, "%s/assets/yyrh_start_image.jpg" % apk_decompile_tmp_dir) else: errorMsg = "找不到闪屏文件,请检查" printlog(errorMsg) HttpUtils.notify_cut_state(gcp_code, "99", errorMsg) return ########### #sdk 关键字 HttpUtils.notify_cut_state(gcp_code, "50", "正在替换渠道参数") sdkParam = Json_Util.ReadJson(config_json_path, "sdk_client_config") printlog(str(sdkParam)) # 替换Androidmanifest.xml中的 package_name值为包名 Replace.replaceAM_package_name(apk_decompile_tmp_dir + DIR_SPLIT + "AndroidManifest.xml") if len(sdkParam): keys = Json_Util.getJsonKeys(sdkParam) for key in keys: # 替换AndroidManifest.xml中的meta-data字段 Replace.replaceAM_Meta_data(apk_decompile_tmp_dir + DIR_SPLIT + "AndroidManifest.xml", key, sdkParam[key]) # 替换assets/YyrhParam.cnf中的关键字 Replace.replaceAssets_Param(apk_decompile_tmp_dir + DIR_SPLIT + "assets" + DIR_SPLIT + "YyrhParam.cnf", key, sdkParam[key]) # 特殊渠道需要特殊处理 Replace.special_replace(sdk_name, apk_decompile_tmp_dir, DIR_SPLIT, key, sdkParam[key]) pass pass ########## #game 关键字 gameParam = Json_Util.ReadJson(config_json_path, "meta_config") printlog("meta_config:%s" % gameParam) if len(gameParam): keys = Json_Util.getJsonKeys(gameParam) for key in keys: if key == "APP_NAME": Replace.replaceString_Appname(apk_decompile_tmp_dir, key, gameParam[key]) elif (key == "version"): version = gameParam[key] elif (key == "v_code"): vCode = gameParam[key] elif (key == "targetSdkVersion"): targetSdkVersion = gameParam[key] # 替换方向 elif (key == "SDK_ORIENTATION"): replaceOrientation = '/bin/sed -i "1,\\$s/sdk_orientation/%s/g" %s/AndroidManifest.xml' % ( gameParam[key], apk_decompile_tmp_dir) printlog("replaceOrientationCmd: %s" % replaceOrientation) os.system(replaceOrientation) Replace.replaceAssets_Param( apk_decompile_tmp_dir + DIR_SPLIT + "assets" + DIR_SPLIT + "YyrhParam.cnf", "SDK_ORIENTATION", gameParam[key]) pass # 修改版本号 HttpUtils.notify_cut_state(gcp_code, "60", "正在修改版本号") yml = "%s/apktool.yml" % apk_decompile_tmp_dir if version != None and version != "": Replace.replaceAM_VersionName(yml, version) if vCode != None and vCode != "": ret = CommonUtils.isMatchRegExp(vCode, "0123456789") if ret == False: errorMsg = "游戏版本号,需为纯数字" printlog(errorMsg) HttpUtils.notify_cut_state(gcp_code, "99", errorMsg) return Replace.replaceAM_VersionCode(yml, vCode) if targetSdkVersion != None and targetSdkVersion != "": ret = CommonUtils.isMatchRegExp(targetSdkVersion, "0123456789") if ret == False: errorMsg = "游戏targetSdkVersion,需为纯数字" printlog(errorMsg) HttpUtils.notify_cut_state(gcp_code, "99", errorMsg) return Replace.replaceYml_targetSdkVersion(yml, targetSdkVersion) Replace.replaceYml_minSdkVersion(yml) mergeWXPay(sdk_root_dir + DIR_SPLIT + sdk_name + DIR_SPLIT + sdk_name, apk_decompile_tmp_dir) HttpUtils.notify_cut_state(gcp_code, "70", "正在替换游戏资源") # 替换游戏资源 resourcepaths = Json_Util.ReadJson(config_json_path, "game_resource_replace") printlog(("游戏替换资源预路径:%s" % resourcepaths)) if resourcepaths != None and resourcepaths != "": for resourcepath in resourcepaths: path = resourcepath[1] if not os.path.exists(path) and path != "": errorMsg = "游戏资源不存在:%s" % path printlog(errorMsg) HttpUtils.notify_cut_state(gcp_code, "99", errorMsg) return Replace_GameRes_Utils.ReplaceGameResource(apk_decompile_tmp_dir, resourcepaths) dst_dir = "%s%s" % (apk_utils.getOutPutDir(), DIR_SPLIT) channels = Json_Util.ReadJson(config_json_path, "channels") Replace.replaceAM_Meta_data("%s/AndroidManifest.xml" % apk_decompile_tmp_dir, Contants.getSdkChannelid(), channels) outputApkPath = "%s_unsigned.apk" % apk_decompile_tmp_dir signedApkPath = "%s_signed.apk" % apk_decompile_tmp_dir zipalignApkPath = "%s_zipaligned.apk" % apk_decompile_tmp_dir # 再编译 HttpUtils.notify_cut_state(gcp_code, "80", "正在回编译APK") recompile(apk_decompile_tmp_dir, outputApkPath) HttpUtils.notify_cut_state(gcp_code, "90", "正在重签名,对齐APK") # 重签名 resignApk(outputApkPath, keystorepath, storepass, alias, keypass, signedApkPath) # 对齐 zipalignApk(signedApkPath, zipalignApkPath) apkDstPath = "%sg%s_%s_%s_%s.apk" % (dst_dir, g_id, sdk_name, package_name, gcp_code) apkDstDir = os.path.dirname(apkDstPath) if not os.path.exists(apkDstDir): os.makedirs(apkDstDir) shutil.copyfile(zipalignApkPath, apkDstPath) HttpUtils.notify_cut_state(gcp_code, "100", "切包成功(点击复制链接)") time_end = time.time() time_c = time_end - time_start printlog("APK路径:%s" % apkDstPath) printlog("切包总用时:%s秒" % time_c) except Exception as err: printlog(errorMsg) HttpUtils.notify_cut_state(gcp_code, "99", errorMsg) isSuccess = False printlog("cut error occur:%s" % err) printlog(traceback.format_exc()) finally: printlog("clear temp dir") # 删除中间生成的目录和文件 if apk_decompile_tmp_dir: File.safeFileDelete(os.path.dirname(apk_decompile_tmp_dir)) return isSuccess if __name__ == "__main__": ret, msg = mergeYml("sdk.yml", "/Volumes/forsdk/sdk/develop/921sdk_cut/script/P0000005/apktool.yml") if not ret: print(msg) # # eg. python merge_apk_v2.py config.json #