|
@@ -1,1323 +0,0 @@
|
|
-# 安卓游戏打包脚本
|
|
|
|
-# 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
|
|
|
|
-import zipfile
|
|
|
|
-import time
|
|
|
|
-import datetime
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-ignoreLauncher = ['jm_ysdk', 'jm_yijie', 'jm_quick', 'jm_beiyu', 'jm_xq_jrtt','jm_zy_ysdk']
|
|
|
|
-adaptApp = ['yjzx']
|
|
|
|
-startTime = ''
|
|
|
|
-
|
|
|
|
-def pack(game, sdk, config):
|
|
|
|
- config['cache'] = uuid.uuid1()
|
|
|
|
- subChannel = config['subChannel']
|
|
|
|
- print('game = %s, sdk = %s, subChannel = %s, ...' % (game,sdk,subChannel))
|
|
|
|
-
|
|
|
|
- # 解包
|
|
|
|
- ret = decomplie(game, sdk, subChannel, config)
|
|
|
|
- if ret:
|
|
|
|
- return ret
|
|
|
|
- # 删除旧代码
|
|
|
|
- ret = removeOldCode(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 = copyIcon(game, sdk, subChannel, config)
|
|
|
|
-
|
|
|
|
- ret = packJar(game, sdk, subChannel, config)
|
|
|
|
- if ret:
|
|
|
|
- return ret
|
|
|
|
-
|
|
|
|
- # 增加配置文件
|
|
|
|
- ret = createJmhyProperties(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
|
|
|
|
- # 清理产生的中间文件
|
|
|
|
- if config['clearCache']:
|
|
|
|
- clearTemp(game, sdk, subChannel, config)
|
|
|
|
-
|
|
|
|
- endTime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
|
|
- d1 = datetime.datetime.strptime(endTime, '%Y-%m-%d %H:%M:%S')
|
|
|
|
- d2 = datetime.datetime.strptime(startTime, '%Y-%m-%d %H:%M:%S')
|
|
|
|
- d = d1 - d2
|
|
|
|
- print ('开始时间:' + startTime)
|
|
|
|
- print ('结束时间:' + endTime)
|
|
|
|
- print ('用时:{}'.format(config_utils.getTime(d.seconds)))
|
|
|
|
- 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', 'Jcat', 'Adsdk')
|
|
|
|
- #file_utils.deleteFolder(codePath)
|
|
|
|
- allFiles = []
|
|
|
|
- allFiles = file_utils.list_files(codePath, allFiles)
|
|
|
|
- for f in allFiles:
|
|
|
|
- fpath, fname = os.path.split(f) #分离文件名和路径
|
|
|
|
- if fname == 'R.smali' or fname.startswith('R$'):
|
|
|
|
- continue
|
|
|
|
- os.remove(f)
|
|
|
|
- #print('remove %s' % f)
|
|
|
|
- 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):
|
|
|
|
- print('res path %s' % path)
|
|
|
|
- if (path.startswith('drawable') or path.startswith('mipmap')) and path.endswith('-v4'):
|
|
|
|
- print('merge path %s' % path)
|
|
|
|
- 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'''
|
|
|
|
- if resFile.endswith('.DS_Store'):
|
|
|
|
- continue
|
|
|
|
- #print('readAllRes -- > ' + os.path.join(absPath, resFile))
|
|
|
|
-
|
|
|
|
- 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...')
|
|
|
|
- #适配一剑斩仙
|
|
|
|
- if game in adaptApp:
|
|
|
|
- print('适配一剑斩仙...')
|
|
|
|
- decomplieAssetsPath = file_utils.getFullPath(decompliePath, 'assets')
|
|
|
|
- skinZipPath = os.path.join(decomplieAssetsPath, 'skin.zip')
|
|
|
|
- skinPath = os.path.join(decomplieAssetsPath, 'skin')
|
|
|
|
- if os.path.exists(skinZipPath):
|
|
|
|
- with zipfile.ZipFile(skinZipPath) as zf:
|
|
|
|
- zf.extractall(decomplieAssetsPath)
|
|
|
|
- print('create unzip skin' )
|
|
|
|
- assetsPath = file_utils.getFullPath(channelPath, 'assets')
|
|
|
|
- decomplieAssetsPath = file_utils.getFullPath(decompliePath, 'assets')
|
|
|
|
- if os.path.exists(assetsPath):
|
|
|
|
- ret = file_utils.copyFileAllDir(assetsPath, decomplieAssetsPath)
|
|
|
|
- with zipfile.ZipFile(skinZipPath, 'w') as z:
|
|
|
|
- for root, dirs, files in os.walk(skinPath):
|
|
|
|
- for single_file in files:
|
|
|
|
- filepath = os.path.join(root, single_file)
|
|
|
|
- print ('create zip ---> ' + filepath)
|
|
|
|
- temPath = 'skin/' + single_file
|
|
|
|
- z.write(filepath, temPath)
|
|
|
|
- z.close()
|
|
|
|
- else:
|
|
|
|
- print('normal 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
|
|
|
|
- else:
|
|
|
|
- print('normal 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_temp.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 jmad.properties...')
|
|
|
|
- decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
|
- configJson = os.path.join(decompliePath, 'assets', 'jmad.properties')
|
|
|
|
- jsonText = json.dumps(config['configData'], ensure_ascii=False)
|
|
|
|
- file_utils.createFile(configJson, jsonText)
|
|
|
|
- return 0
|
|
|
|
-
|
|
|
|
-def createJmhyProperties(game, sdk, subChannel, config):
|
|
|
|
- '''
|
|
|
|
- 创建jmhy.properties
|
|
|
|
- '''
|
|
|
|
- print('create jmad.properties...')
|
|
|
|
- propValue = config['properties']
|
|
|
|
- decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
|
- properties = os.path.join(decompliePath, 'assets', 'jmad.properties')
|
|
|
|
- content = ''
|
|
|
|
- for key in propValue:
|
|
|
|
- content = '%s%s=%s\n' % (content, key, propValue[key])
|
|
|
|
- file_utils.createFile(properties, content)
|
|
|
|
- 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'])
|
|
|
|
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
|
|
|
|
- activity = xml_utils.getLauncherActivityName(manifest)
|
|
|
|
- if activity == 'com.jmhy.sdk.template.LauncherActivity':
|
|
|
|
- print('add launcher already exist...')
|
|
|
|
- return 1
|
|
|
|
- # 添加关联资源
|
|
|
|
- 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...')
|
|
|
|
-
|
|
|
|
- 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 copyIcon(game, sdk, subChannel, config):
|
|
|
|
- '''
|
|
|
|
- 复制icon到res ,一键登录使用
|
|
|
|
- '''
|
|
|
|
- if config['changeIcon']:
|
|
|
|
- decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
|
- decomplieResPath = file_utils.getFullPath(decompliePath, 'res')
|
|
|
|
- iconPath = os.path.join(decomplieResPath, 'mipmap-xhdpi', 'common_sdk_icon.png')
|
|
|
|
- desPath = os.path.join(decomplieResPath, 'drawable-hdpi', 'jm_cmcc_icon.png')
|
|
|
|
- file_utils.copyFile(iconPath, desPath)
|
|
|
|
- else:
|
|
|
|
- decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
|
- decomplieResPath = file_utils.getFullPath(decompliePath, 'res')
|
|
|
|
- desPath = os.path.join(decomplieResPath, 'drawable-hdpi', 'jm_cmcc_icon.png')
|
|
|
|
-
|
|
|
|
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
|
|
|
|
- icon = xml_utils.getApplicationAttr(manifest, 'icon')
|
|
|
|
- tag = icon[1:].split("/")
|
|
|
|
- if len(tag) < 1:
|
|
|
|
- return
|
|
|
|
- hdpi = ['-xhdpi', '-xxhdpi', '-xxxhdpi']
|
|
|
|
- for h in hdpi:
|
|
|
|
- p1 = '%s%s' % (tag[0],h)
|
|
|
|
- png = tag[1] + ".png"
|
|
|
|
- iconPath = os.path.join(decomplieResPath, p1, png)
|
|
|
|
- if os.path.exists(iconPath):
|
|
|
|
- print ('iconPath = ' + iconPath)
|
|
|
|
- file_utils.copyFile(iconPath, desPath)
|
|
|
|
- break
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-def formatHex(millisecond):
|
|
|
|
- '''
|
|
|
|
- 将毫秒转为16进制,4位格式
|
|
|
|
- '''
|
|
|
|
- timeHex = str(hex(millisecond)).upper()
|
|
|
|
- timeHex = timeHex[2:]
|
|
|
|
- formatHex = ''
|
|
|
|
- if len(timeHex) == 3:
|
|
|
|
- formatHex = '0x0' + timeHexaddLauncher
|
|
|
|
- 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'])
|
|
|
|
- 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)
|
|
|
|
-
|
|
|
|
- # 生成R文件
|
|
|
|
- print('生成R文件 ...')
|
|
|
|
- if config['aapt2disable']:
|
|
|
|
- aapt = file_utils.getAAPTPath()
|
|
|
|
- ret = file_utils.getExecPermission(aapt)
|
|
|
|
- if ret:
|
|
|
|
- return ret
|
|
|
|
- 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
|
|
|
|
- else:
|
|
|
|
- # compile
|
|
|
|
- aapt = file_utils.getAAPT2Path()
|
|
|
|
- ret = file_utils.getExecPermission(aapt)
|
|
|
|
- if ret:
|
|
|
|
- return ret
|
|
|
|
-
|
|
|
|
- print('compiled res ...')
|
|
|
|
- complieResPath = os.path.join(compliePath, 'compiled')
|
|
|
|
- complieResCmd = '"%s" compile --dir "%s" -o "%s"' % (aapt, decomplieResPath, complieResPath)
|
|
|
|
- file_utils.execFormatCmd(complieResCmd)
|
|
|
|
-
|
|
|
|
- # unzip
|
|
|
|
- print('unzip compiled res ...')
|
|
|
|
- unzipResPath = os.path.join(compliePath, 'aapt2_res')
|
|
|
|
- with zipfile.ZipFile(complieResPath) as zf:
|
|
|
|
- zf.extractall(unzipResPath)
|
|
|
|
- print('create unzip %s' % unzipResPath)
|
|
|
|
-
|
|
|
|
- # link
|
|
|
|
- print('link res ...')
|
|
|
|
- outApk = os.path.join(compliePath, 'res.apk')
|
|
|
|
- linkResCmd = '"%s" link -o "%s" -I "%s" --manifest "%s" --java "%s" --auto-add-overlay' % (aapt, outApk, androidPlatforms, manifest, compliePath)
|
|
|
|
- for filename in os.listdir(unzipResPath):
|
|
|
|
- linkResCmd += ' %s' % filename
|
|
|
|
- print('link cmd len is %s' % len(linkResCmd))
|
|
|
|
- ret = file_utils.execFormatCmd(linkResCmd, unzipResPath)
|
|
|
|
- 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.8 -target 1.8 -encoding UTF-8 "%s"' % RSourceFile
|
|
|
|
- ret = file_utils.execFormatCmd(complieRCmd)
|
|
|
|
- if ret:
|
|
|
|
- return ret
|
|
|
|
-
|
|
|
|
- # 生成dex
|
|
|
|
- print('dex R.class ...')
|
|
|
|
- outDex = os.path.join(compliePath, 'classes.dex')
|
|
|
|
- if config['aapt2disable']:
|
|
|
|
- dx = file_utils.getDxPath()
|
|
|
|
- dexCmd = '--dex --no-warning --output="%s" "%s"' % (outDex, compliePath)
|
|
|
|
- else:
|
|
|
|
- dx = file_utils.getD8Path()
|
|
|
|
- clazz = os.path.join(packagePath, '*.class')
|
|
|
|
- dexCmd = '--lib "%s" --output "%s" %s' % (androidPlatforms, compliePath, clazz)
|
|
|
|
- ret = file_utils.execJarCmd(dx, dexCmd)
|
|
|
|
- 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, 'd "%s" -o "%s"' % (outDex, outPath))
|
|
|
|
- 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')
|
|
|
|
-
|
|
|
|
- if not os.path.exists(outPath):
|
|
|
|
- os.makedirs(outPath)
|
|
|
|
- if config['aapt2disable']:
|
|
|
|
- dx = file_utils.getDxPath()
|
|
|
|
- dexCmd = '--dex --multi-dex --no-warning --output="%s"' % outPath
|
|
|
|
- else:
|
|
|
|
- dx = file_utils.getD8Path()
|
|
|
|
- androidPlatforms = file_utils.getAndroidCompileToolPath()
|
|
|
|
- dexCmd = '--lib "%s" --output "%s"' % (androidPlatforms, outPath)
|
|
|
|
-
|
|
|
|
- # 找到所有lib依赖
|
|
|
|
- sdkPath = file_utils.getFullSDKPath(sdk)
|
|
|
|
- print('sdkpath --> ' + sdkPath)
|
|
|
|
- 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)
|
|
|
|
- elif 'default' in libConf:
|
|
|
|
- libList = libConf['default']
|
|
|
|
- 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, 'd "%s" -o "%s"' % (outDex, outPath))
|
|
|
|
- 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):
|
|
|
|
- '''
|
|
|
|
- 打包Log jar
|
|
|
|
- '''
|
|
|
|
- decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
|
- outPath = file_utils.getFullPath(decompliePath, 'gen')
|
|
|
|
-
|
|
|
|
- if not os.path.exists(outPath):
|
|
|
|
- os.makedirs(outPath)
|
|
|
|
-
|
|
|
|
- if config['aapt2disable']:
|
|
|
|
- dx = file_utils.getDxPath()
|
|
|
|
- dexCmd = '--dex --multi-dex --no-warning --output="%s"' % outPath
|
|
|
|
- else:
|
|
|
|
- dx = file_utils.getD8Path()
|
|
|
|
- androidPlatforms = file_utils.getAndroidCompileToolPath()
|
|
|
|
- dexCmd = '--lib "%s" --output "%s"' % (androidPlatforms, outPath)
|
|
|
|
-
|
|
|
|
- # 找到所有lib依赖
|
|
|
|
- sdkPath = file_utils.getFullLogSDKPath(logSdk)
|
|
|
|
- libs = os.path.join(sdkPath, 'libs')
|
|
|
|
- libConfig = os.path.join(libs, 'config.json')
|
|
|
|
-
|
|
|
|
- # 存在配置文件
|
|
|
|
- if os.path.exists(libConfig):
|
|
|
|
- jsonText = file_utils.readFile(libConfig)
|
|
|
|
- libList = json.loads(jsonText)
|
|
|
|
- for jar in libList:
|
|
|
|
- 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)
|
|
|
|
-
|
|
|
|
- # 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, 'd "%s" -o "%s"' % (outDex, outPath))
|
|
|
|
- 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 packReativeJar(game, sdk, subChannel, config, logSdk):
|
|
|
|
- '''
|
|
|
|
- 打包Reative jar
|
|
|
|
- '''
|
|
|
|
- decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
|
|
|
|
- outPath = file_utils.getFullPath(decompliePath, 'gen')
|
|
|
|
-
|
|
|
|
- if not os.path.exists(outPath):
|
|
|
|
- os.makedirs(outPath)
|
|
|
|
-
|
|
|
|
- if config['aapt2disable']:
|
|
|
|
- dx = file_utils.getDxPath()
|
|
|
|
- dexCmd = '--dex --multi-dex --no-warning --output="%s"' % outPath
|
|
|
|
- else:
|
|
|
|
- dx = file_utils.getD8Path()
|
|
|
|
- androidPlatforms = file_utils.getAndroidCompileToolPath()
|
|
|
|
- dexCmd = '--lib "%s" --output "%s"' % (androidPlatforms, outPath)
|
|
|
|
-
|
|
|
|
- # 找到所有lib依赖
|
|
|
|
- sdkPath = file_utils.getFullLogSDKPath(logSdk)
|
|
|
|
- libs = os.path.join(sdkPath, 'libs')
|
|
|
|
- libConfig = os.path.join(libs, 'config.json')
|
|
|
|
-
|
|
|
|
- # 存在配置文件
|
|
|
|
- if os.path.exists(libConfig):
|
|
|
|
- jsonText = file_utils.readFile(libConfig)
|
|
|
|
- libList = json.loads(jsonText)
|
|
|
|
- for jar in libList:
|
|
|
|
- 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)
|
|
|
|
-
|
|
|
|
- # 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, 'd "%s" -o "%s"' % (outDex, outPath))
|
|
|
|
- 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']
|
|
|
|
- else:
|
|
|
|
- targetSdkVersion = 26
|
|
|
|
- print('changeVersion versionCode=%s,versionName=%s,targetSdkVersion=%s' % (versionCode, versionName, 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'])
|
|
|
|
-
|
|
|
|
- useAppt2 = ' --use-aapt2'
|
|
|
|
- if config['aapt2disable']:
|
|
|
|
- useAppt2 = ''
|
|
|
|
-
|
|
|
|
- return file_utils.execJarCmd(apktoolPath, 'b -f "%s" -o "%s"%s' % (decompliePath, outApk, useAppt2))
|
|
|
|
-
|
|
|
|
-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...')
|
|
|
|
- print('game = %s, sdk = %s, subChannel = %s, ...' % (game,sdk,subChannel))
|
|
|
|
- path = os.path.join(file_utils.getCurrentPath(), 'keystore', 'key.json')
|
|
|
|
- jsonText = file_utils.readFile(path)
|
|
|
|
- signConfig = json.loads(jsonText)
|
|
|
|
- keystore = {}
|
|
|
|
- for key in signConfig.keys():
|
|
|
|
- print(key)
|
|
|
|
- if game.find(key) > -1:
|
|
|
|
- if sdk in signConfig[key]:
|
|
|
|
- keystore = signConfig[key][sdk]
|
|
|
|
- else:
|
|
|
|
- keystore = signConfig['default']
|
|
|
|
- else:
|
|
|
|
- keystore = signConfig['default']
|
|
|
|
-
|
|
|
|
- # if game in signConfig:
|
|
|
|
- # if sdk in signConfig[game]:
|
|
|
|
- # keystore = signConfig[game][sdk]
|
|
|
|
- # 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 = ''
|
|
|
|
- host = ''
|
|
|
|
- if 'appid' in properties:
|
|
|
|
- appid = properties['appid']
|
|
|
|
- if 'appkey' in properties:
|
|
|
|
- appkey = properties['appkey']
|
|
|
|
- if 'host' in properties:
|
|
|
|
- host = properties['host']
|
|
|
|
-
|
|
|
|
- return file_utils.execJarCmd(walle, 'put -e version=%s,agent=%s,appid=%s,appkey=%s,host=%s "%s" "%s"' % (config_utils.getDate(), properties['agent'], appid, appkey, host, 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):
|
|
|
|
- '''
|
|
|
|
- 控制台打包
|
|
|
|
- '''
|
|
|
|
- print('--------- yfsdk ---------')
|
|
|
|
-
|
|
|
|
- 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
|
|
|
|
- global startTime
|
|
|
|
- startTime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
|
|
-
|
|
|
|
- # 读取配置
|
|
|
|
- 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
|
|
|