12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631 |
- # 安卓游戏打包脚本
- # 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 re
- import file_utils
- import xml_utils
- import smali_utils
- import config_utils
- import game_utils
- import common_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
- # 删除一些不支持的属性
- 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)
- # 复制icon图标到res
- ret = copyIcon(game, sdk, subChannel, config)
- # if ret:
- # return ret
- # 打包lib依赖
- ret = packJar(game, sdk, subChannel, config)
- if ret:
- return ret
- # 继承JMApplication
- common_utils.changeApplication(
- game, sdk, subChannel, config, 'com.jmhy.sdk.common.JMApplication')
- # 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
- # 游戏独立处理
- ret = doGameScript(game, sdk, config)
- if ret:
- return ret
- # 格式化Xml
- # ret = formatXml(game, sdk, subChannel, 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
- # oaid sdk
- ret = addOaidSdk(game, sdk, subChannel, config, "1.0.10")
- 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
- # 更改HardwareAccelerated属性
- ret = replaceAmHardwareAccelerated(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
- ret = copyApk2OutDir(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)
- cacheGameApk = file_utils.getCacheGameApk(game, config['random'], sdk)
- 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"' % (cacheGameApk, 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)
- 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' or abi == 'arm64-v8a':
- print('append : ' + abi)
- 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、启动图等
- '''
- random = config['random']
- channelPath = file_utils.getSubChannelPath(game, sdk, random, 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 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
- random = config['random']
- channelPath = file_utils.getSubChannelPath(game, sdk, random, 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 doGameScript(game, sdk, config):
- '''
- 执行游戏相关特殊处理脚本
- '''
- channelPath = file_utils.getFullPath('game_script', game)
- targetScript = os.path.join(channelPath, 'game_script.py')
- print(targetScript + "--------")
- if not os.path.exists(targetScript):
- print('game_script no exists')
- return 0
- print('doGameScript...')
- sys.path.append(channelPath)
- module = importlib.import_module('game_script')
- ret = module.execute(game, sdk, config)
- sys.path.remove(channelPath)
- return ret
- def addLogSdk(game, sdk, subChannel, config, logSdk):
- # 拷贝jniLibs
- decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
- sdkPath = ''
- print('add log sdk, sdk:' + sdk)
- if sdk == 'jm_beta_sdk' or sdk == 'beta_sdk':
- sdkPath = file_utils.getFullLogSDKPath(logSdk, True)
- else:
- 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' or abi == 'arm64-v8a':
- 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
- changePlaceholders(game, sdk, subChannel, config)
- return packLogJar(game, sdk, subChannel, config, logSdk)
- def addOaidSdk(game, sdk, subChannel, config, oaidVersion):
- sdkPath = file_utils.getFullOaidSDKPath(oaidVersion)
- decompliePath = file_utils.getDecompliePath(
- game, sdk, subChannel, config['cache'])
- # 拷贝assets
- print('copy oaid 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
- # 拷贝jniLibs
- print('copy oaid 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 oaid 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 packOaidJar(game, sdk, subChannel, config, oaidVersion)
- 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 --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)
- 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:
- print('getMultiDexPath ...')
- 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依赖
- if sdk == 'jm_beta_sdk' or sdk == 'beta_sdk':
- sdkPath = file_utils.getFullLogSDKPath(logSdk, True)
- else:
- 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 packOaidJar(game, sdk, subChannel, config, oaidVerion):
- '''
- 打包oaid 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.getFullOaidSDKPath(oaidVerion)
- 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 oaid jar ...')
- dexCmd += ' ' + os.path.join(libs, 'miit_mdid_%s.jar' % oaidVerion)
- 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 or game == key:
- if sdk in signConfig[key]:
- keystore = signConfig[key][sdk]
- break
- 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.getSiginedApkPath(
- game, sdk, subChannel, config['cache'])
- storeFile = os.path.join(file_utils.getCurrentPath(),
- 'keystore', keystore['storeFile'])
- # 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.getSiginedApkPath(
- game, sdk, subChannel, config['cache'])
- walleApk = file_utils.getWalleApkPath(
- game, sdk, subChannel, config['cache'])
- 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, walleApk))
- def clearTemp(game, sdk, subChannel, config):
- '''
- 清空中间产生的文件
- '''
- print('clear temp...')
- targetApkPath = file_utils.getTargetApkPath(game, sdk, config['cache'])
- if os.path.exists(targetApkPath):
- file_utils.deleteFolder(targetApkPath)
- decompliePath = file_utils.getDecompliePath(
- game, sdk, subChannel, config['cache'])
- if os.path.exists(decompliePath):
- file_utils.deleteFolder(decompliePath)
- random = config['random']
- channelPath = file_utils.getChannelPath(game, random, sdk)
- if os.path.exists(channelPath):
- file_utils.deleteFolder(channelPath)
- 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, config, subChannel):
- '''
- 控制台打包
- '''
- cache_game_apk_path = file_utils.getCacheGameApk(game, config['random'], sdk)
- print('cache game apk path %s' % cache_game_apk_path)
- if not os.path.exists(cache_game_apk_path):
- 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')
- # 读取配置
- random = config['random']
- channelPath = file_utils.getChannelPath(game, random, 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))
- subChannelPath = os.path.join(channelPath, subChannel)
- print('------subChannelPath 目录清空:%s -------' % subChannelPath)
- file_utils.deleteFolder(subChannelPath)
- return 0
- def formatXml(game, sdk, subChannel, config):
- decompliePath = file_utils.getDecompliePath(
- game, sdk, subChannel, config['cache'])
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
- return xml_utils.formatXml(manifest)
- def copyApk2OutDir(game, sdk, subChannel, config):
- walleApk = file_utils.getWalleApkPath(
- game, sdk, subChannel, config['cache'])
- releaseApkPath = ""
- if 'outName' in config and 'outPath' in config:
- if not os.path.exists(config['outPath']):
- os.makedirs(config['outPath'])
- releaseApkPath = os.path.join(
- config['outPath'], config['outName'] + '.apk')
- elif 'outName' in config:
- releaseApkPath = file_utils.getRenameApkPath(
- game, sdk, config['cache'], config['outName'])
- print('------生成正式包路径 %s -------' % releaseApkPath)
- file_utils.copyFile(walleApk, releaseApkPath)
- def replaceAmHardwareAccelerated(game, sdk, subChannel, config):
- decompliePath = file_utils.getDecompliePath(
- game, sdk, subChannel, config['cache'])
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
- lines = open(manifest).readlines()
- fp = open(manifest, "w")
- isReplaced = False
- for l in lines:
- if isReplaced == False and l.find("android:hardwareAccelerated=") >= 0:
- isReplace = True
- l = re.sub("android:hardwareAccelerated=\"false\"",
- "android:hardwareAccelerated=\"true\"", l)
- fp.write(l)
- fp.close()
- pass
- def openFile(file, mode):
- return open(file, mode, encoding='UTF-8')
|