123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047 |
- import file_utils
- import xml_utils
- import smali_utils
- import config_utils_record
- import game_utils
- import os
- import os.path
- import json
- import sys
- import importlib
- import uuid
- def pack(game, sdk, config):
- config['cache'] = uuid.uuid1()
- subChannel = config['subChannel']
-
- ret = decomplie(game, sdk, subChannel, config)
- if ret:
- return ret
- if 'deleteList' in config:
-
- ret = removeOldRes(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
-
- ret = mergeDrawableRes(game, sdk, subChannel, config)
- if ret:
- return ret
-
- ret = removeSameRes(game, sdk, subChannel, config)
- if ret:
- return ret
-
- 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
-
- ret = addMetaData(game, sdk, subChannel, config)
- if ret:
- return ret
-
- ret = copyAppRes(game, sdk, subChannel, config)
- if ret:
- return ret
-
- ret = addConfig(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
- else:
- config['packageName'] = getPackageName(game, sdk, subChannel, config)
-
- if 'name' in config and config['name'] != '':
- ret = changeAppName(game, sdk, subChannel, config)
- if ret:
- return ret
-
- 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 = packJar(game, sdk, subChannel, config)
- if ret:
- return ret
-
- ret = doSDKPostScript(game, sdk, config)
- if ret:
- return ret
-
- ret = game_utils.sdkLebianChange(game, sdk, config)
- if ret:
- return ret
-
- ret = doGamePostScript(game, sdk, config)
- if ret:
- return ret
-
- ret = generateNewRFile(game, sdk, subChannel, config)
- if ret:
- return ret
-
- 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
-
- 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)
- 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 removeOldRes(game, sdk, subChannel, config):
- '''
- 删除旧资源
- '''
- print('delete res ...')
- decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
- for subPath in config['deleteList']:
- resPath = os.path.join(decompliePath, subPath)
- if os.path.exists(resPath):
- os.remove(resPath)
- print('delete ' + resPath)
- return 0
- def removeOldCode(game, sdk, subChannel, config):
- '''
- 删除旧代码
- '''
- decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
- codePath = os.path.join(decompliePath, 'smali', 'com', 'jmhy', 'lib', 'record')
- file_utils.deleteFolder(codePath)
- return 0
- def copyRes(game, sdk, subChannel, config):
- '''
- 复制res资源
- '''
-
- 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)
-
- 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
-
- 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):
- '''
- 移除相同的资源
- '''
-
- 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'])
-
- 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
-
- 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):
-
- 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):
- '''
- 删除一些不支持的配置
- '''
-
- 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):
- '''
- 更改包名
- '''
-
- 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 getPackageName(game, sdk, subChannel, config):
- decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
- packageName = xml_utils.getPackageName(manifest)
- return packageName
- def changeAppName(game, sdk, subChannel, config):
- '''
- 更改app名
- '''
-
- name = config['name']
- resName = 'shanshen_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)
-
- 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 = 'record_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:
- print('configData is null')
- return 0
- print('add config.json...')
- decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
- configJson = os.path.join(decompliePath, 'assets', 'record_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):
- '''
- 添加启动图
- '''
- 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 = os.path.join(file_utils.getCurrentPath(), 'internal_shanshen')
- 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.shanshen.sdk.template.LauncherActivity')
-
- launcherActivity = os.path.join(decompliePath, 'smali', 'com', 'shanshen', 'sdk', 'template', 'LauncherActivity.smali')
- file_utils.replaceContent(launcherActivity, '{class}', activity)
- print('change launcher %s to %s' % (activity, 'com.shanshen.sdk.template.LauncherActivity'))
-
- 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 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
-
- 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
-
- 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
-
- 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
-
-
- 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)
-
- dexCmd = '--dex --multi-dex --no-warning --output="%s"' % outPath
-
- 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)
-
- if splitDex:
- dexCmd += ' ' + file_utils.getMultiDexPath()
-
- print('packageing all jar ...')
- ret = file_utils.execJarCmd(dx, dexCmd)
- if ret:
- return ret
-
-
- 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 splitDex(game, sdk, subChannel, config):
- '''
- 分割dex
- '''
-
-
-
- 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)
-
-
-
-
- maxFuncNum = 64000
- currFucNum = 0
- totalFucNum = 0
- currDexIndex = 1
- allRefs = []
-
- 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
-
-
- 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
-
- 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'])
-
-
- 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_record.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_record.checkConfig(config):
- return 1
-
- config_utils_record.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
|