|
@@ -0,0 +1,1125 @@
|
|
|
+# 安卓游戏打包脚本
|
|
|
+# 1.用apktool解包
|
|
|
+# 2.复制res资源、assets资源、jniLib资源
|
|
|
+# 3.修改包名、app名、渠道文件
|
|
|
+# 4.添加app icon、合并AndroidManifest文件
|
|
|
+# 5.添加app启动图,修改AndroidManifest文件
|
|
|
+# 6.生成新的R文件
|
|
|
+# 7.将新的R文件编译,生成dex,再用baksmali生成smali,替换旧的R文件
|
|
|
+# 8.将jar资源打包成dex,再用baksmali生成smali,拷贝到smali目录下
|
|
|
+# 9.统计方法量,并分割dex(将smali文件拷贝到目录smali_classes2)
|
|
|
+# 10.用apktool回包
|
|
|
+# 11.重新签名
|
|
|
+import file_utils
|
|
|
+import xml_utils
|
|
|
+import smali_utils
|
|
|
+import config_utils
|
|
|
+import game_utils
|
|
|
+import os
|
|
|
+import os.path
|
|
|
+import json
|
|
|
+import sys
|
|
|
+import importlib
|
|
|
+import uuid
|
|
|
+
|
|
|
+ignoreLauncher = ['jm_ysdk', 'jm_yijie', 'jm_quick', 'jm_beiyu', 'jm_xq_jrtt']
|
|
|
+
|
|
|
+def pack(game, sdk, config):
|
|
|
+ config['cache'] = uuid.uuid1()
|
|
|
+ subChannel = config['subChannel']
|
|
|
+ # 解包
|
|
|
+ ret = decomplie(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 删除旧代码
|
|
|
+ ret = removeOldCode(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 删除一些不支持的属性
|
|
|
+ ret = removeNoSupportAttr(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 删除一些不支持的配置
|
|
|
+ ret = fixUnSupportConfig(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 合并Drawable-v4目录
|
|
|
+ ret = mergeDrawableRes(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 移除相同的资源
|
|
|
+ ret = removeSameRes(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 复制res资源
|
|
|
+ ret = copyRes(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 合并主文件
|
|
|
+ ret = mergeManifestRes(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 替换占位符
|
|
|
+ ret = changePlaceholders(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 添加meta-data
|
|
|
+ ret = addMetaData(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 增加配置文件
|
|
|
+ ret = addConfig(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 复制app res资源
|
|
|
+ ret = copyAppRes(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 更改包名
|
|
|
+ if 'packageName' in config and config['packageName'] != '':
|
|
|
+ ret = changePackageName(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 更改app名
|
|
|
+ if 'name' in config and config['name'] != '':
|
|
|
+ ret = changeAppName(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 更改app icon
|
|
|
+ if config['changeIcon']:
|
|
|
+ ret = changeAppIcon(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 添加启动图操作
|
|
|
+ if config['addLauncher']:
|
|
|
+ ret = addLauncher(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 添加多图标
|
|
|
+ #ret = addMoreIcon(game, sdk, subChannel, config)
|
|
|
+ #if ret:
|
|
|
+ # return ret
|
|
|
+ # 打包lib依赖
|
|
|
+ ret = packJar(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # sdk脚本处理
|
|
|
+ ret = doSDKPostScript(game, sdk, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 乐变sdk的特殊处理
|
|
|
+ ret = game_utils.sdkLebianChange(game, sdk, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 游戏脚本处理
|
|
|
+ ret = doGamePostScript(game, sdk, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # log sdk
|
|
|
+ if 'logSdk' in config:
|
|
|
+ for log in config['logSdk']:
|
|
|
+ ret = addLogSdk(game, sdk, subChannel, config, log)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 生成R文件
|
|
|
+ ret = generateNewRFile(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 添加MultiDex支持
|
|
|
+ if config['splitDex']:
|
|
|
+ ret = splitDex(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 更改版本号
|
|
|
+ ret = changeVersion(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 回编译
|
|
|
+ ret = recomplie(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 对齐apk
|
|
|
+ ret = alignApk(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 签名
|
|
|
+ ret = apksignerApk(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 添加渠道信息
|
|
|
+ ret = addChannel(game, sdk, subChannel, config)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 清理产生的中间文件
|
|
|
+ if config['clearCache']:
|
|
|
+ clearTemp(game, sdk, subChannel, config)
|
|
|
+
|
|
|
+ return 0
|
|
|
+
|
|
|
+def decomplie(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 解包
|
|
|
+ '''
|
|
|
+ apktoolPath = file_utils.getApkToolPath()
|
|
|
+ gamePath = file_utils.getFullGameApk(game)
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+
|
|
|
+ if os.path.exists(decompliePath):
|
|
|
+ print('delete decomplie folder...')
|
|
|
+ file_utils.deleteFolder(decompliePath)
|
|
|
+
|
|
|
+ print('decomplie apk...')
|
|
|
+
|
|
|
+ return file_utils.execJarCmd(apktoolPath, 'd -f "%s" -o "%s"' % (gamePath, decompliePath))
|
|
|
+
|
|
|
+def removeOldCode(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 删除旧代码
|
|
|
+ '''
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ codePath = os.path.join(decompliePath, 'smali', 'com', 'jmhy', 'sdk')
|
|
|
+ file_utils.deleteFolder(codePath)
|
|
|
+ return 0
|
|
|
+
|
|
|
+def copyRes(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 复制res资源
|
|
|
+ '''
|
|
|
+ # 拷贝sdk资源
|
|
|
+ print('copy res...')
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ sdkPath = file_utils.getFullSDKPath(sdk)
|
|
|
+ resPath = os.path.join(sdkPath, 'res')
|
|
|
+ decomplieResPath = file_utils.getFullPath(decompliePath, 'res')
|
|
|
+
|
|
|
+ for d in os.listdir(resPath):
|
|
|
+ copyResWithType(resPath, decomplieResPath, d)
|
|
|
+
|
|
|
+ # 拷贝assets
|
|
|
+ print('copy assets...')
|
|
|
+ assetsPath = file_utils.getFullPath(sdkPath, 'assets')
|
|
|
+ decomplieAssetsPath = file_utils.getFullPath(decompliePath, 'assets')
|
|
|
+ if os.path.exists(assetsPath):
|
|
|
+ ret = file_utils.copyFileAllDir(assetsPath, decomplieAssetsPath)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ # 拷贝jniLib
|
|
|
+ print('copy jniLibs...')
|
|
|
+ jniPath = file_utils.getFullPath(sdkPath, 'jniLibs')
|
|
|
+ decomplieJniPath = file_utils.getFullPath(decompliePath, 'lib')
|
|
|
+ abiFilters = []
|
|
|
+ if os.path.exists(decomplieJniPath):
|
|
|
+ for abi in os.listdir(decomplieJniPath):
|
|
|
+ if abi == 'armeabi-v7a' or abi == 'armeabi':
|
|
|
+ abiFilters.append(abi)
|
|
|
+ else:
|
|
|
+ abiFilters = ['armeabi-v7a']
|
|
|
+
|
|
|
+ if os.path.exists(jniPath):
|
|
|
+ ret = file_utils.copyFileAllDir(jniPath, decomplieJniPath, False, abiFilters)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ return 0
|
|
|
+
|
|
|
+def mergeDrawableRes(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 合并Drawable-v4目录
|
|
|
+ '''
|
|
|
+ print('merge drawable path...')
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ resPath = os.path.join(decompliePath, 'res')
|
|
|
+
|
|
|
+ for path in os.listdir(resPath):
|
|
|
+ if path.startswith('drawable') and path.endswith('-v4'):
|
|
|
+ v4DrawablePath = os.path.join(resPath, path)
|
|
|
+ drawablePath = os.path.join(resPath, path[:-3])
|
|
|
+
|
|
|
+ if os.path.exists(drawablePath):
|
|
|
+ ret = file_utils.copyFileAllDir(v4DrawablePath, drawablePath, True)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ else:
|
|
|
+ os.rename(v4DrawablePath, drawablePath)
|
|
|
+ return 0
|
|
|
+
|
|
|
+def removeSameRes(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 移除相同的资源
|
|
|
+ '''
|
|
|
+ # 读取sdk的资源
|
|
|
+ print('remove same res...')
|
|
|
+ sdkPath = file_utils.getFullSDKPath(sdk)
|
|
|
+ sdkResPath = os.path.join(sdkPath, 'res')
|
|
|
+ resList = []
|
|
|
+ for path in os.listdir(sdkResPath):
|
|
|
+ if not path.startswith('values'):
|
|
|
+ continue
|
|
|
+
|
|
|
+ absPath = os.path.join(sdkResPath, path)
|
|
|
+ for resFile in os.listdir(absPath):
|
|
|
+ '''if not resFile.startswith('jm_'):
|
|
|
+ continue'''
|
|
|
+
|
|
|
+ resList = xml_utils.readAllRes(os.path.join(absPath, resFile), resList)
|
|
|
+
|
|
|
+ if len(resList) == 0:
|
|
|
+ print('no same res found')
|
|
|
+ return 0
|
|
|
+
|
|
|
+ # 移除相同的资源
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ resPath = os.path.join(decompliePath, 'res')
|
|
|
+ for path in os.listdir(resPath):
|
|
|
+ if not path.startswith('values'):
|
|
|
+ continue
|
|
|
+
|
|
|
+ absPath = os.path.join(resPath, path)
|
|
|
+ for resFile in os.listdir(absPath):
|
|
|
+ xml_utils.removeSameRes(os.path.join(absPath, resFile), resList)
|
|
|
+
|
|
|
+ return 0
|
|
|
+
|
|
|
+def mergeManifestRes(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 合并主文件
|
|
|
+ '''
|
|
|
+ print('merge AndroidManifest...')
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
|
|
|
+ sdkPath = file_utils.getFullSDKPath(sdk)
|
|
|
+ libManifest = file_utils.getFullPath(sdkPath, 'manifest.xml')
|
|
|
+ return xml_utils.mergeManifestRes(manifest, libManifest)
|
|
|
+
|
|
|
+def copyAppRes(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 拷贝app的资源,比如app icon、启动图等
|
|
|
+ '''
|
|
|
+ channelPath = file_utils.getSubChannelPath(game, sdk, subChannel)
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+
|
|
|
+ # assets
|
|
|
+ print('copy assets...')
|
|
|
+ assetsPath = file_utils.getFullPath(channelPath, 'assets')
|
|
|
+ decomplieAssetsPath = file_utils.getFullPath(decompliePath, 'assets')
|
|
|
+ if os.path.exists(assetsPath):
|
|
|
+ ret = file_utils.copyFileAllDir(assetsPath, decomplieAssetsPath)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ # icon
|
|
|
+ print('copy icon...')
|
|
|
+ ret = copyAppResWithType(decompliePath, channelPath, 'icon')
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 启动图
|
|
|
+ print('copy splash...')
|
|
|
+ ret = copyAppResWithType(decompliePath, channelPath, 'splash')
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ # 其他图片
|
|
|
+ print('copy image...')
|
|
|
+ ret = copyAppResWithType(decompliePath, channelPath, 'image')
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ return 0
|
|
|
+
|
|
|
+def copyAppResWithType(decompliePath, channelPath, typeName):
|
|
|
+ decomplieResPath = os.path.join(decompliePath, 'res')
|
|
|
+ iconPath = os.path.join(channelPath, typeName)
|
|
|
+ if not os.path.exists(iconPath):
|
|
|
+ print('dir "%s" not exists' % iconPath)
|
|
|
+ return 0
|
|
|
+ for d in os.listdir(iconPath):
|
|
|
+ ret = copyResWithType(iconPath, decomplieResPath, d)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ return 0
|
|
|
+
|
|
|
+def copyResWithType(resPath, decomplieResPath, typeName):
|
|
|
+ # appt的打包目录会带-v4后缀
|
|
|
+ resDir = os.path.join(resPath, typeName)
|
|
|
+ target = os.path.join(decomplieResPath, typeName)
|
|
|
+ targetV4 = os.path.join(decomplieResPath, typeName + '-v4')
|
|
|
+ if not os.path.exists(target) and not os.path.exists(targetV4):
|
|
|
+ os.makedirs(target)
|
|
|
+ return file_utils.copyFileAllDir(resDir, target, False)
|
|
|
+ elif not os.path.exists(target):
|
|
|
+ return file_utils.copyFileAllDir(resDir, targetV4, False)
|
|
|
+ else:
|
|
|
+ return file_utils.copyFileAllDir(resDir, target, False)
|
|
|
+
|
|
|
+def removeNoSupportAttr(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 删除一些不支持的属性
|
|
|
+ '''
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
|
|
|
+
|
|
|
+ xml_utils.removeRootAttr(manifest, 'compileSdkVersion')
|
|
|
+ xml_utils.removeRootAttr(manifest, 'compileSdkVersionCodename')
|
|
|
+
|
|
|
+ return 0
|
|
|
+
|
|
|
+def fixUnSupportConfig(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 删除一些不支持的配置
|
|
|
+ '''
|
|
|
+ # 检查minSdkVersion
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ yml = os.path.join(decompliePath, 'apktool.yml')
|
|
|
+
|
|
|
+ minSdkVersion = 15
|
|
|
+ file_utils.changeMinSdkVersion(yml, minSdkVersion)
|
|
|
+
|
|
|
+ resPath = os.path.join(decompliePath, 'res')
|
|
|
+
|
|
|
+ tag = '-v'
|
|
|
+ for res in os.listdir(resPath):
|
|
|
+ print('res = ' + res)
|
|
|
+ if res.startswith('values') and tag in res:
|
|
|
+ start = res.index(tag)
|
|
|
+ version = res[start+len(tag):]
|
|
|
+ if not version.isdigit():
|
|
|
+ continue
|
|
|
+
|
|
|
+ version = int(version)
|
|
|
+ print('version = %d' % version)
|
|
|
+ if version < minSdkVersion:
|
|
|
+ unSopportPath = os.path.join(resPath, res)
|
|
|
+ print('unSopportPath = ' + unSopportPath)
|
|
|
+ file_utils.deleteFolder(unSopportPath)
|
|
|
+ print('deleteFolder = ' + unSopportPath)
|
|
|
+
|
|
|
+ return 0
|
|
|
+
|
|
|
+def changePackageName(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 更改包名
|
|
|
+ '''
|
|
|
+ # 全局替换AndroidManifest里面的包名
|
|
|
+ newPackageName = config['packageName']
|
|
|
+
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
|
|
|
+ packageName = xml_utils.getPackageName(manifest)
|
|
|
+
|
|
|
+ xml_utils.changePackageName(manifest, newPackageName)
|
|
|
+ print('change package name %s --> %s' % (packageName, newPackageName))
|
|
|
+ return 0
|
|
|
+
|
|
|
+def changeAppName(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 更改app名
|
|
|
+ '''
|
|
|
+ # 生成string.xml文件
|
|
|
+ name = config['name']
|
|
|
+
|
|
|
+ resName = 'common_sdk_name'
|
|
|
+ if 'outName' in config:
|
|
|
+ resName = resName + '_' + config['outName']
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ stringFile = os.path.join(decompliePath, 'res', 'values', 'sdk_strings.xml')
|
|
|
+ content = '<?xml version="1.0" encoding="utf-8"?><resources><string name="%s">%s</string></resources>' % (resName, name)
|
|
|
+ file_utils.createFile(stringFile, content)
|
|
|
+
|
|
|
+ # 修改主文件的app名的值
|
|
|
+ manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
|
|
|
+ xml_utils.changeAppName(manifest, '@string/%s' % resName)
|
|
|
+ print('change app name %s' % name)
|
|
|
+ return 0
|
|
|
+
|
|
|
+def changeAppIcon(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 更改app icon
|
|
|
+ '''
|
|
|
+ print('change app icon...')
|
|
|
+ resName = 'common_sdk_icon'
|
|
|
+
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
|
|
|
+ xml_utils.changeAppIcon(manifest, '@mipmap/%s' % resName)
|
|
|
+ return 0
|
|
|
+
|
|
|
+def addMetaData(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 添加meta-data
|
|
|
+ '''
|
|
|
+ if 'metaData' not in config:
|
|
|
+ return 0
|
|
|
+
|
|
|
+ print('add meta-data...')
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
|
|
|
+ xml_utils.addMetaData(manifest, config['metaData'])
|
|
|
+ return 0
|
|
|
+
|
|
|
+def addConfig(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 添加config.json
|
|
|
+ '''
|
|
|
+ if 'configData' not in config:
|
|
|
+ return 0
|
|
|
+
|
|
|
+ print('add config.json...')
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ configJson = os.path.join(decompliePath, 'assets', 'jmhy_config.json')
|
|
|
+ jsonText = json.dumps(config['configData'], ensure_ascii=False)
|
|
|
+ file_utils.createFile(configJson, jsonText)
|
|
|
+ return 0
|
|
|
+
|
|
|
+def changePlaceholders(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 处理掉占位符
|
|
|
+ '''
|
|
|
+ if 'placeholders' not in config:
|
|
|
+ return 0
|
|
|
+
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
|
|
|
+
|
|
|
+ placeholders = config['placeholders']
|
|
|
+ for placeholder in placeholders:
|
|
|
+ oldText = '${%s}' % placeholder
|
|
|
+ newText = placeholders[placeholder]
|
|
|
+ print('change placeholder %s -> %s' % (oldText, newText))
|
|
|
+ file_utils.replaceContent(manifest, oldText, newText)
|
|
|
+
|
|
|
+ return 0
|
|
|
+
|
|
|
+def addLauncher(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 添加启动图
|
|
|
+ '''
|
|
|
+ # ysdk的特殊处理
|
|
|
+ if sdk in ignoreLauncher:
|
|
|
+ return 0
|
|
|
+
|
|
|
+ channelPath = file_utils.getSubChannelPath(game, sdk, subChannel)
|
|
|
+ splashPath = os.path.join(channelPath, 'splash')
|
|
|
+ if len(os.listdir(splashPath)) == 0:
|
|
|
+ print('dir splash is empty')
|
|
|
+ return 0
|
|
|
+
|
|
|
+ print('add launcher...')
|
|
|
+ # 添加关联资源
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ internalPath = file_utils.getFullInternalPath()
|
|
|
+ ret = copyAppResWithType(decompliePath, internalPath, 'launcher_res')
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ # 拷贝代码
|
|
|
+ print('copy launcher code...')
|
|
|
+ codePath = os.path.join(internalPath, 'launcher_code', 'smali')
|
|
|
+ smaliPath = file_utils.getFullPath(decompliePath, 'smali')
|
|
|
+ ret = file_utils.copyFileAllDir(codePath, smaliPath)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ # 修改主文件信息
|
|
|
+ print('change launcher config...')
|
|
|
+ manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
|
|
|
+ orientation = xml_utils.getScreenOrientation(manifest)
|
|
|
+ activity = xml_utils.removeLauncherActivity(manifest)
|
|
|
+ xml_utils.addLauncherActivity(manifest, orientation, 'com.jmhy.sdk.template.LauncherActivity')
|
|
|
+
|
|
|
+ # 修改跳转的
|
|
|
+ launcherActivity = os.path.join(decompliePath, 'smali', 'com', 'jmhy', 'sdk', 'template', 'LauncherActivity.smali')
|
|
|
+ file_utils.replaceContent(launcherActivity, '{class}', activity)
|
|
|
+
|
|
|
+ print('change launcher %s to %s' % (activity, 'com.jmhy.sdk.template.LauncherActivity'))
|
|
|
+
|
|
|
+ # config['oldLauncher'] = activity
|
|
|
+
|
|
|
+ if 'launcherTime' in config:
|
|
|
+ timeHex = formatHex(config['launcherTime'])
|
|
|
+ file_utils.replaceContent(launcherActivity, '0x0BB8', timeHex)
|
|
|
+
|
|
|
+ return 0
|
|
|
+
|
|
|
+def addMoreIcon(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 添加多个图标
|
|
|
+ '''
|
|
|
+ print('add more icon support...')
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ icon = '@mipmap/common_sdk_icon'
|
|
|
+ if not config['changeIcon']:
|
|
|
+ manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
|
|
|
+ icon = xml_utils.getApplicationAttr(manifest, 'icon')
|
|
|
+
|
|
|
+ switchIcon = icon
|
|
|
+ if config['switchIcon']:
|
|
|
+ switchIcon = '@mipmap/common_sdk_icon2'
|
|
|
+
|
|
|
+ manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
|
|
|
+ return xml_utils.addMoreIcon(manifest, icon, switchIcon)
|
|
|
+
|
|
|
+def formatHex(millisecond):
|
|
|
+ '''
|
|
|
+ 将毫秒转为16进制,4位格式
|
|
|
+ '''
|
|
|
+ timeHex = str(hex(millisecond)).upper()
|
|
|
+ timeHex = timeHex[2:]
|
|
|
+ formatHex = ''
|
|
|
+ if len(timeHex) == 3:
|
|
|
+ formatHex = '0x0' + timeHex
|
|
|
+ elif len(timeHex) == 4:
|
|
|
+ formatHex = '0x' + timeHex
|
|
|
+ else:
|
|
|
+ formatHex = '0x0BB8'
|
|
|
+ return formatHex
|
|
|
+
|
|
|
+def doSDKPostScript(game, sdk, config):
|
|
|
+ '''
|
|
|
+ 执行sdk相关特殊处理脚本
|
|
|
+ '''
|
|
|
+ sdkPath = file_utils.getFullSDKPath(sdk)
|
|
|
+ scriptPath = os.path.join(sdkPath, 'script')
|
|
|
+ targetScript = os.path.join(scriptPath, 'sdk_script.py')
|
|
|
+ if not os.path.exists(targetScript):
|
|
|
+ print('sdk_script no exists')
|
|
|
+ return 0
|
|
|
+
|
|
|
+ print('doSDKPostScript...')
|
|
|
+ sys.path.append(scriptPath)
|
|
|
+
|
|
|
+ module = importlib.import_module('sdk_script')
|
|
|
+ ret = module.execute(game, sdk, config)
|
|
|
+
|
|
|
+ sys.path.remove(scriptPath)
|
|
|
+
|
|
|
+ return ret
|
|
|
+
|
|
|
+def doGamePostScript(game, sdk, config):
|
|
|
+ '''
|
|
|
+ 执行游戏相关特殊处理脚本
|
|
|
+ '''
|
|
|
+ channelPath = file_utils.getFullGamePath(game)
|
|
|
+ scriptPath = os.path.join(channelPath, 'script')
|
|
|
+ targetScript = os.path.join(scriptPath, 'game_script.py')
|
|
|
+ if not os.path.exists(targetScript):
|
|
|
+ print('game_script no exists')
|
|
|
+ return 0
|
|
|
+
|
|
|
+ print('doGamePostScript...')
|
|
|
+ sys.path.append(scriptPath)
|
|
|
+
|
|
|
+ module = importlib.import_module('game_script')
|
|
|
+ ret = module.execute(game, sdk, config)
|
|
|
+
|
|
|
+ sys.path.remove(scriptPath)
|
|
|
+
|
|
|
+ return ret
|
|
|
+
|
|
|
+def addLogSdk(game, sdk, subChannel, config, logSdk):
|
|
|
+ # 拷贝jniLibs
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ sdkPath = file_utils.getFullLogSDKPath(logSdk)
|
|
|
+
|
|
|
+ print('copy log jniLibs...')
|
|
|
+ jniPath = file_utils.getFullPath(sdkPath, 'jniLibs')
|
|
|
+ decomplieJniPath = file_utils.getFullPath(decompliePath, 'lib')
|
|
|
+ abiFilters = []
|
|
|
+ if os.path.exists(decomplieJniPath):
|
|
|
+ for abi in os.listdir(decomplieJniPath):
|
|
|
+ if abi == 'armeabi-v7a' or abi == 'armeabi':
|
|
|
+ abiFilters.append(abi)
|
|
|
+ else:
|
|
|
+ abiFilters = ['armeabi-v7a']
|
|
|
+
|
|
|
+ if os.path.exists(jniPath):
|
|
|
+ ret = file_utils.copyFileAllDir(jniPath, decomplieJniPath, False, abiFilters)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ print('merge log AndroidManifest...')
|
|
|
+ libManifest = file_utils.getFullPath(sdkPath, 'manifest.xml')
|
|
|
+ if os.path.exists(libManifest):
|
|
|
+ manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
|
|
|
+ ret = xml_utils.mergeManifestRes(manifest, libManifest)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ return packLogJar(game, sdk, subChannel, config, logSdk)
|
|
|
+
|
|
|
+def generateNewRFile(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 生成新的R文件
|
|
|
+ '''
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ aapt = file_utils.getAAPTPath()
|
|
|
+ androidPlatforms = file_utils.getAndroidCompileToolPath()
|
|
|
+ manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
|
|
|
+ decomplieResPath = file_utils.getFullPath(decompliePath, 'res')
|
|
|
+ compliePath = file_utils.getFullPath(decompliePath, 'gen')
|
|
|
+
|
|
|
+ if not os.path.exists(compliePath):
|
|
|
+ os.makedirs(compliePath)
|
|
|
+
|
|
|
+ ret = file_utils.getExecPermission(aapt)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ # 生成R文件
|
|
|
+ print('create R.java ...')
|
|
|
+ createRCmd = '"%s" p -f -m -J "%s" -S "%s" -I "%s" -M "%s"' % (aapt, compliePath, decomplieResPath, androidPlatforms, manifest)
|
|
|
+ ret = file_utils.execFormatCmd(createRCmd)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ # 编译R文件
|
|
|
+ print('complie R.java ...')
|
|
|
+ packageName = xml_utils.getPackageName(manifest)
|
|
|
+ packagePath = file_utils.getPackagePath(compliePath, packageName)
|
|
|
+ RSourceFile = os.path.join(packagePath, 'R.java')
|
|
|
+ complieRCmd = 'javac -source 1.7 -target 1.7 -encoding UTF-8 "%s"' % RSourceFile
|
|
|
+ ret = file_utils.execFormatCmd(complieRCmd)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ # 生成dex
|
|
|
+ print('dex R.class ...')
|
|
|
+ dx = file_utils.getDxPath()
|
|
|
+ outDex = os.path.join(compliePath, 'classes.dex')
|
|
|
+ ret = file_utils.execJarCmd(dx, '--dex --output="%s" "%s"' % (outDex, compliePath))
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ # 反向dex生成smali
|
|
|
+ # 存放在out目录
|
|
|
+ print('baksmali classes.dex ...')
|
|
|
+ baksmaliPath = file_utils.getBaksmaliPath()
|
|
|
+ outPath = file_utils.getFullPath(decompliePath, 'out')
|
|
|
+ ret = file_utils.execJarCmd(baksmaliPath, '-o "%s" "%s"' % (outPath, outDex))
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ # 将生成的文件拷贝到目标目录
|
|
|
+ print('copy R.smali ...')
|
|
|
+ smaliPath = file_utils.getFullPath(decompliePath, 'smali')
|
|
|
+ file_utils.copyFileAllDir(outPath, smaliPath)
|
|
|
+ return 0
|
|
|
+
|
|
|
+def packJar(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 打包所有的jar
|
|
|
+ '''
|
|
|
+ splitDex = config['splitDex']
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ outPath = file_utils.getFullPath(decompliePath, 'gen')
|
|
|
+ dx = file_utils.getDxPath()
|
|
|
+
|
|
|
+ if not os.path.exists(outPath):
|
|
|
+ os.makedirs(outPath)
|
|
|
+
|
|
|
+ # --no-warning
|
|
|
+ dexCmd = '--dex --multi-dex --no-warning --output="%s"' % outPath
|
|
|
+
|
|
|
+ # 找到所有lib依赖
|
|
|
+ sdkPath = file_utils.getFullSDKPath(sdk)
|
|
|
+ libs = os.path.join(sdkPath, 'libs')
|
|
|
+ libConfig = os.path.join(libs, 'config.json')
|
|
|
+
|
|
|
+ # 存在配置文件
|
|
|
+ if os.path.exists(libConfig):
|
|
|
+ jsonText = file_utils.readFile(libConfig)
|
|
|
+ libConf = json.loads(jsonText)
|
|
|
+ if 'libConfig' in config and config['libConfig'] in libConf:
|
|
|
+ conf = config['libConfig']
|
|
|
+ libList = libConf[conf]
|
|
|
+ for jar in libList:
|
|
|
+ dexCmd += ' ' + os.path.join(libs, jar)
|
|
|
+ else:
|
|
|
+ for jar in os.listdir(libs):
|
|
|
+ if not jar.endswith('.jar'):
|
|
|
+ continue
|
|
|
+ dexCmd += ' ' + os.path.join(libs, jar)
|
|
|
+ else:
|
|
|
+ for jar in os.listdir(libs):
|
|
|
+ if not jar.endswith('.jar'):
|
|
|
+ continue
|
|
|
+ dexCmd += ' ' + os.path.join(libs, jar)
|
|
|
+
|
|
|
+ # multidex.jar
|
|
|
+ if splitDex:
|
|
|
+ dexCmd += ' ' + file_utils.getMultiDexPath()
|
|
|
+
|
|
|
+ # sdk实现类
|
|
|
+ print('packageing all jar ...')
|
|
|
+ dexCmd += ' ' + os.path.join(sdkPath, '%s.jar' % sdk)
|
|
|
+ ret = file_utils.execJarCmd(dx, dexCmd)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ # 反向dex生成smali
|
|
|
+ # 存放在out目录
|
|
|
+ print('baksmali classes.dex ...')
|
|
|
+ outDex = os.path.join(outPath, 'classes.dex')
|
|
|
+ baksmaliPath = file_utils.getBaksmaliPath()
|
|
|
+ outPath = file_utils.getFullPath(decompliePath, 'out')
|
|
|
+
|
|
|
+ ret = file_utils.execJarCmd(baksmaliPath, '-o "%s" "%s"' % (outPath, outDex))
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ # 将生成的文件拷贝到目标目录
|
|
|
+ print('copy all smali ...')
|
|
|
+ smaliPath = file_utils.getFullPath(decompliePath, 'smali')
|
|
|
+ ret = file_utils.copyFileAllDir(outPath, smaliPath, True)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ return 0
|
|
|
+
|
|
|
+def packLogJar(game, sdk, subChannel, config, logSdk):
|
|
|
+ '''
|
|
|
+ 打包所有的jar
|
|
|
+ '''
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ outPath = file_utils.getFullPath(decompliePath, 'gen')
|
|
|
+ dx = file_utils.getDxPath()
|
|
|
+
|
|
|
+ if not os.path.exists(outPath):
|
|
|
+ os.makedirs(outPath)
|
|
|
+
|
|
|
+ # --no-warning
|
|
|
+ dexCmd = '--dex --multi-dex --no-warning --output="%s"' % outPath
|
|
|
+
|
|
|
+ # 找到所有lib依赖
|
|
|
+ sdkPath = file_utils.getFullLogSDKPath(logSdk)
|
|
|
+ libs = os.path.join(sdkPath, 'libs')
|
|
|
+ for jar in os.listdir(libs):
|
|
|
+ if not jar.endswith('.jar'):
|
|
|
+ continue
|
|
|
+ dexCmd += ' ' + os.path.join(libs, jar)
|
|
|
+
|
|
|
+ # sdk实现类
|
|
|
+ print('packageing all log jar ...')
|
|
|
+ dexCmd += ' ' + os.path.join(sdkPath, '%s.jar' % logSdk)
|
|
|
+ ret = file_utils.execJarCmd(dx, dexCmd)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ # 反向dex生成smali
|
|
|
+ # 存放在out目录
|
|
|
+ print('baksmali classes.dex ...')
|
|
|
+ outDex = os.path.join(outPath, 'classes.dex')
|
|
|
+ baksmaliPath = file_utils.getBaksmaliPath()
|
|
|
+ outPath = file_utils.getFullPath(decompliePath, 'out')
|
|
|
+
|
|
|
+ ret = file_utils.execJarCmd(baksmaliPath, '-o "%s" "%s"' % (outPath, outDex))
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ # 将生成的文件拷贝到目标目录
|
|
|
+ print('copy all log smali ...')
|
|
|
+ smaliPath = file_utils.getFullPath(decompliePath, 'smali')
|
|
|
+ ret = file_utils.copyFileAllDir(outPath, smaliPath, True)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ return 0
|
|
|
+
|
|
|
+def splitDex(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 分割dex
|
|
|
+ '''
|
|
|
+ # 判断是否已经存在application
|
|
|
+ # 存在,则往原application添加内容
|
|
|
+ # 不存在,则拷贝一个默认的android.support.multidex.MultiDexApplication
|
|
|
+ print('add MultiDex support...')
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
|
|
|
+ application = xml_utils.getApplicationAttr(manifest, 'name')
|
|
|
+ if application is None:
|
|
|
+ ret = xml_utils.changeApplicationAttr(manifest, 'name', 'android.support.multidex.MultiDexApplication')
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+ else:
|
|
|
+ smaliPath = os.path.join(decompliePath, 'smali')
|
|
|
+ applicationFile = file_utils.getPackagePath(smaliPath, application)
|
|
|
+ applicationFile += '.smali'
|
|
|
+ ret = changeApplicationDex(applicationFile)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ return splitSmali(game, sdk, subChannel, config, application)
|
|
|
+
|
|
|
+def changeApplicationDex(file):
|
|
|
+ '''
|
|
|
+ 修改application的smali文件,增加MultiDex操作
|
|
|
+ '''
|
|
|
+ index = file_utils.getApplicationSmaliIndex(file)
|
|
|
+ file_utils.insertApplicationSmali(file, index)
|
|
|
+ return 0
|
|
|
+
|
|
|
+def splitSmali(game, sdk, subChannel, config, application):
|
|
|
+ '''
|
|
|
+ 如果函数上限超过限制,自动拆分smali,以便生成多个dex文件
|
|
|
+ '''
|
|
|
+ print('splitSmali...')
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ smaliPath = os.path.join(decompliePath, 'smali')
|
|
|
+
|
|
|
+ appPackage = None
|
|
|
+ if application:
|
|
|
+ appPackage = application[:application.rfind('.')]
|
|
|
+ appPackage = appPackage.replace('.', '/')
|
|
|
+
|
|
|
+ allFiles = []
|
|
|
+ allFiles = file_utils.list_files(smaliPath, allFiles)
|
|
|
+
|
|
|
+ #print('file count is %d' % len(allFiles))
|
|
|
+
|
|
|
+ #maxFuncNum = 65535
|
|
|
+ # 留一点空间,防止计算误差
|
|
|
+ maxFuncNum = 64000
|
|
|
+ currFucNum = 0
|
|
|
+ totalFucNum = 0
|
|
|
+
|
|
|
+ currDexIndex = 1
|
|
|
+
|
|
|
+ allRefs = []
|
|
|
+
|
|
|
+ #保证Application等类在第一个classex.dex文件中
|
|
|
+ for f in allFiles:
|
|
|
+ f = f.replace('\\', '/')
|
|
|
+ if (appPackage and appPackage in f) or '/android/support/multidex' in f:
|
|
|
+ currFucNum += smali_utils.get_smali_method_count(f, allRefs)
|
|
|
+
|
|
|
+ totalFucNum = currFucNum
|
|
|
+ for f in allFiles:
|
|
|
+ f = f.replace('\\', '/')
|
|
|
+ if not f.endswith('.smali'):
|
|
|
+ continue
|
|
|
+
|
|
|
+ if (appPackage and appPackage in f) or '/android/support/multidex' in f:
|
|
|
+ continue
|
|
|
+
|
|
|
+ thisFucNum = smali_utils.get_smali_method_count(f, allRefs)
|
|
|
+ totalFucNum += thisFucNum
|
|
|
+
|
|
|
+ #print('%d # %d ==> %s' % (thisFucNum, currDexIndex, f))
|
|
|
+ #print('totalFucNum is %d' % totalFucNum)
|
|
|
+ if currFucNum + thisFucNum >= maxFuncNum:
|
|
|
+ currFucNum = thisFucNum
|
|
|
+ currDexIndex += 1
|
|
|
+ newDexPath = os.path.join(decompliePath, 'smali_classes%d' % currDexIndex)
|
|
|
+ os.makedirs(newDexPath)
|
|
|
+ else:
|
|
|
+ currFucNum += thisFucNum
|
|
|
+
|
|
|
+ if currDexIndex > 1:
|
|
|
+ newDexPath = os.path.join(decompliePath, 'smali_classes%d' % currDexIndex)
|
|
|
+ targetFile = newDexPath + f[len(smaliPath):]
|
|
|
+ file_utils.copyFile(f, targetFile, True)
|
|
|
+ return 0
|
|
|
+
|
|
|
+def changeVersion(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 更改版本号
|
|
|
+ '''
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ yml = os.path.join(decompliePath, 'apktool.yml')
|
|
|
+
|
|
|
+ versionCode = None
|
|
|
+ versionName = None
|
|
|
+ targetSdkVersion = None
|
|
|
+ if 'versionCode' in config:
|
|
|
+ versionCode = config['versionCode']
|
|
|
+ if 'versionName' in config:
|
|
|
+ versionName = config['versionName']
|
|
|
+ if 'targetSdkVersion' in config:
|
|
|
+ targetSdkVersion = config['targetSdkVersion']
|
|
|
+
|
|
|
+ return file_utils.changeVersion(yml, versionCode, versionName, targetSdkVersion)
|
|
|
+
|
|
|
+def recomplie(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 回编译
|
|
|
+ '''
|
|
|
+ print('recomplie apk...')
|
|
|
+ apktoolPath = file_utils.getApkToolPath()
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ outApk = file_utils.getOutApkPath(game, sdk, subChannel, config['cache'])
|
|
|
+
|
|
|
+ return file_utils.execJarCmd(apktoolPath, 'b -f "%s" -o "%s"' % (decompliePath, outApk))
|
|
|
+
|
|
|
+def alignApk(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 对齐apk
|
|
|
+ '''
|
|
|
+ print('align apk...')
|
|
|
+ outApk = file_utils.getOutApkPath(game, sdk, subChannel, config['cache'])
|
|
|
+ alignApk = file_utils.getAlignApkPath(game, sdk, subChannel, config['cache'])
|
|
|
+ alignapkTool = file_utils.getAlignPath()
|
|
|
+ if os.path.exists(alignApk):
|
|
|
+ os.remove(alignApk)
|
|
|
+
|
|
|
+ ret = file_utils.getExecPermission(alignapkTool)
|
|
|
+ if ret:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ # zipalign.exe -v -p 4 input.apk output.apk
|
|
|
+ return file_utils.execFormatCmd('"%s" -f -p 4 "%s" "%s"' % (alignapkTool, outApk, alignApk))
|
|
|
+
|
|
|
+def apksignerApk(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 签名apk
|
|
|
+ '''
|
|
|
+ print('sign apk...')
|
|
|
+ path = os.path.join(file_utils.getCurrentPath(), 'keystore', 'key.json')
|
|
|
+ jsonText = file_utils.readFile(path)
|
|
|
+ signConfig = json.loads(jsonText)
|
|
|
+ keystore = {}
|
|
|
+ if game in signConfig:
|
|
|
+ if sdk in signConfig[game] and subChannel in signConfig[game][sdk]:
|
|
|
+ keystore = signConfig[game][sdk][subChannel]
|
|
|
+ else:
|
|
|
+ keystore = signConfig['default']
|
|
|
+ else:
|
|
|
+ keystore = signConfig['default']
|
|
|
+
|
|
|
+ print('storeFile is "%s"' % keystore['storeFile'])
|
|
|
+
|
|
|
+ apksigner = file_utils.getApksignerPath()
|
|
|
+ alignApk = file_utils.getAlignApkPath(game, sdk, subChannel, config['cache'])
|
|
|
+ signedApk = file_utils.getSignApkPath(game, sdk, subChannel, config['cache'])
|
|
|
+ storeFile = os.path.join(file_utils.getCurrentPath(), 'keystore', keystore['storeFile'])
|
|
|
+
|
|
|
+ if 'outName' in config and 'outPath' in config:
|
|
|
+ if not os.path.exists(config['outPath']):
|
|
|
+ os.makedirs(config['outPath'])
|
|
|
+
|
|
|
+ signedApk = os.path.join(config['outPath'], config['outName'] + '.apk')
|
|
|
+ elif 'outName' in config:
|
|
|
+ signedApk = file_utils.getRenameApkPath(game, sdk, config['cache'], config['outName'])
|
|
|
+
|
|
|
+ # java -jar apksigner.jar sign --ks key.jks --ks-key-alias releasekey --ks-pass pass:pp123456 --key-pass pass:pp123456 --out output.apk input.apk
|
|
|
+ v2disable = ''
|
|
|
+ if 'v2disable' in config and config['v2disable']:
|
|
|
+ v2disable = ' --v2-signing-enabled=false'
|
|
|
+
|
|
|
+ return file_utils.execJarCmd(apksigner, 'sign%s --ks "%s" --ks-key-alias %s --ks-pass pass:%s --key-pass pass:%s --out "%s" "%s"' % (v2disable, storeFile, keystore['keyAlias'], keystore['storePassword'], keystore['keyPassword'], signedApk, alignApk))
|
|
|
+
|
|
|
+def addChannel(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 添加渠道信息
|
|
|
+ '''
|
|
|
+ if 'v2disable' in config and config['v2disable']:
|
|
|
+ return 0
|
|
|
+
|
|
|
+ walle = file_utils.getWallePath()
|
|
|
+ signedApk = file_utils.getSignApkPath(game, sdk, subChannel, config['cache'])
|
|
|
+ if 'outName' in config and 'outPath' in config:
|
|
|
+ signedApk = os.path.join(config['outPath'], config['outName'] + '.apk')
|
|
|
+ elif 'outName' in config:
|
|
|
+ signedApk = file_utils.getRenameApkPath(game, sdk, config['cache'], config['outName'])
|
|
|
+
|
|
|
+ properties = config['properties']
|
|
|
+
|
|
|
+ appid = ''
|
|
|
+ appkey = ''
|
|
|
+ if 'appid' in properties:
|
|
|
+ appid = properties['appid']
|
|
|
+ if 'appkey' in properties:
|
|
|
+ appkey = properties['appkey']
|
|
|
+
|
|
|
+ return file_utils.execJarCmd(walle, 'put -e version=%s,agent=%s,appid=%s,appkey=%s "%s" "%s"' % (config_utils.getDate(), properties['agent'], appid, appkey, signedApk, signedApk))
|
|
|
+
|
|
|
+def clearTemp(game, sdk, subChannel, config):
|
|
|
+ '''
|
|
|
+ 清空中间产生的文件
|
|
|
+ '''
|
|
|
+ print('clear temp...')
|
|
|
+ alignApk = file_utils.getAlignApkPath(game, sdk, subChannel, config['cache'])
|
|
|
+ outApk = file_utils.getOutApkPath(game, sdk, subChannel, config['cache'])
|
|
|
+ if os.path.exists(alignApk):
|
|
|
+ os.remove(alignApk)
|
|
|
+ if os.path.exists(outApk):
|
|
|
+ os.remove(outApk)
|
|
|
+
|
|
|
+ decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
+ file_utils.deleteFolder(decompliePath)
|
|
|
+
|
|
|
+ print('clear temp end')
|
|
|
+
|
|
|
+def packConsoleInput():
|
|
|
+ '''
|
|
|
+ 控制台打包
|
|
|
+ '''
|
|
|
+ if len(sys.argv) < 3:
|
|
|
+ print('argument is missing')
|
|
|
+ return 1
|
|
|
+
|
|
|
+ # 校验参数
|
|
|
+ game = sys.argv[1]
|
|
|
+ sdk = sys.argv[2]
|
|
|
+ # 可选参数,没有则默认打全部渠道
|
|
|
+ subChannel = None
|
|
|
+ if len(sys.argv) > 3:
|
|
|
+ subChannel = sys.argv[3]
|
|
|
+
|
|
|
+ return packConsole(game, sdk, subChannel)
|
|
|
+
|
|
|
+def packConsole(game, sdk, subChannel):
|
|
|
+ '''
|
|
|
+ 控制台打包
|
|
|
+ '''
|
|
|
+ if not os.path.exists(file_utils.getFullGameApk(game)):
|
|
|
+ print('game "%s" not exists' % game)
|
|
|
+ return 1
|
|
|
+ if not os.path.exists(file_utils.getFullSDKPath(sdk)):
|
|
|
+ print('sdk "%s" not exists' % sdk)
|
|
|
+ return 1
|
|
|
+
|
|
|
+ # 读取配置
|
|
|
+ channelPath = file_utils.getChannelPath(game, sdk)
|
|
|
+ configPath = os.path.join(channelPath, 'config.json')
|
|
|
+ if not os.path.exists(configPath):
|
|
|
+ print('%s not exists' % configPath)
|
|
|
+ return 1
|
|
|
+
|
|
|
+ jsonText = file_utils.readFile(configPath)
|
|
|
+ config = json.loads(jsonText)
|
|
|
+
|
|
|
+ # 检查参数
|
|
|
+ if not config_utils.checkConfig(config):
|
|
|
+ return 1
|
|
|
+
|
|
|
+ # 处理参数
|
|
|
+ config_utils.replaceArgs(config)
|
|
|
+
|
|
|
+ successCount = 0
|
|
|
+ failureCount = 0
|
|
|
+ if type(config) == dict:
|
|
|
+ if subChannel is None or config['subChannel'] == subChannel:
|
|
|
+ ret = pack(game, sdk, config)
|
|
|
+ if ret:
|
|
|
+ failureCount += 1
|
|
|
+ else:
|
|
|
+ successCount += 1
|
|
|
+ else:
|
|
|
+ print('subChannel "%s" no found' % subChannel)
|
|
|
+ return 1
|
|
|
+ elif type(config) == list:
|
|
|
+ found = False
|
|
|
+ for itemConfig in config:
|
|
|
+ if subChannel is None or itemConfig['subChannel'] == subChannel:
|
|
|
+ found = True
|
|
|
+ ret = pack(game, sdk, itemConfig)
|
|
|
+ if ret:
|
|
|
+ failureCount += 1
|
|
|
+ else:
|
|
|
+ successCount += 1
|
|
|
+
|
|
|
+ if not found:
|
|
|
+ print('subChannel "%s" no found' % subChannel)
|
|
|
+ return 1
|
|
|
+
|
|
|
+ print('success %d, failure %d' % (successCount, failureCount))
|
|
|
+
|
|
|
+ return 0
|