123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902 |
- # 安卓游戏打包脚本
- # 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 datetime
- import importlib
- import json
- import os
- import os.path
- import re
- import sys
- import uuid
- import zipfile
- import common_utils
- import config_utils
- import file_utils
- import game_utils
- import smali_utils
- import xml_utils
- 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()
- sub_channel = config['subChannel']
- print('game = %s, sdk = %s, subChannel = %s, ...' % (game, sdk, sub_channel))
- # 解包
- print('解包')
- ret = decomplie(game, sdk, sub_channel, config)
- if ret:
- return ret
- if is_refactor_sdk(config):
- return pack_v2(game, sdk, sub_channel, config)
- else:
- return pack_v1(game, sdk, sub_channel, config)
- def pack_v1(game, sdk, sub_channel, config):
- # 删除旧代码
- print('删除旧代码')
- ret = handle_jm_old_code(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 删除一些不支持的属性
- print('删除一些不支持的属性')
- ret = remove_no_support_attr(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 删除一些不支持的配置
- print('删除一些不支持的配置')
- ret = fix_un_support_config(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 合并Drawable-v4目录
- ret = merge_drawable_res(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 移除相同的资源
- ret = remove_same_values_res(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 复制res资源
- ret = copy_res(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 合并主文件
- ret = merge_manifest_res(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 替换占位符
- ret = change_placeholders(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 添加meta-data
- ret = add_meta_data(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 增加配置文件
- ret = add_v1_config(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 复制app res资源
- ret = copy_app_res(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 更改包名
- if 'packageName' in config and config['packageName'] != '':
- ret = change_package_name(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 更改app名
- if 'name' in config and config['name'] != '':
- ret = change_app_name(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 更改app icon
- if config['changeIcon']:
- ret = change_app_icon(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 添加启动图操作
- if config['addLauncher']:
- ret = add_launcher(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 添加多图标
- # ret = addMoreIcon(game, sdk, subChannel, config)
- # 复制icon图标到res
- copy_icon(game, sdk, sub_channel, config)
- # 打包lib依赖
- ret = pack_jar(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 修改继承Application
- print('修改继承Application')
- common_utils.change_application(game, sdk, sub_channel, config, 'com.jmhy.sdk.common.JMApplication')
- # sdk脚本处理
- ret = do_sdk_post_script(game, sdk, config)
- if ret:
- return ret
- # 乐变sdk的特殊处理
- ret = game_utils.sdkLebianChange(game, sdk, config)
- if ret:
- return ret
- # 游戏脚本处理
- ret = do_game_post_script(game, sdk, config)
- if ret:
- return ret
- # 游戏独立处理
- ret = do_game_script(game, sdk, config)
- if ret:
- return ret
- # log sdk
- if 'logSdk' in config:
- for log in config['logSdk']:
- ret = add_log_sdk(game, sdk, sub_channel, config, log)
- if ret:
- return ret
- # oaid sdk
- ret = addOaidSdk(game, sdk, sub_channel, config, "1.0.10")
- if ret:
- return ret
- # 生成R文件
- '''ret = generateNewRFile(game, sdk, subChannel, config)
- if ret:
- return ret'''
- # 添加MultiDex支持
- if config['splitDex']:
- ret = split_dex(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 更改版本号
- ret = changeVersion(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 更改HardwareAccelerated属性
- replace_hardware_accelerated(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 回编译
- ret = recomplie(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 对齐apk
- ret = alignApk(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 签名
- ret = apk_signer_apk(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 添加渠道信息
- ret = add_channel(game, sdk, sub_channel, config)
- if ret:
- return ret
- copy_v1_apk_2_out_dir(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 清理产生的中间文件
- if config['clearCache']:
- clearTemp(game, sdk, sub_channel, 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 pack_v2(game, sdk, sub_channel, config):
- # 删除旧代码
- ret = handle_qingshi_old_code(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 删除一些不支持的属性
- ret = remove_no_support_attr(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 删除一些不支持的配置
- ret = fix_un_support_config(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 合并Drawable-v4目录
- ret = merge_drawable_res(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 移除旧sdk资源
- remove_v2_old_res(game, sdk, sub_channel, config)
- # 移除旧abi文件
- remove_v2_old_abi(game, sdk, sub_channel, config)
- # 移除相同的资源
- ret = remove_same_values_res(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 复制res资源
- ret = copy_res(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 合并主文件
- ret = merge_manifest_res(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 替换占位符
- ret = change_placeholders(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 添加meta-data
- ret = add_meta_data(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 增加配置文件
- ret = add_v2_config(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 复制app res资源
- ret = copy_app_res(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 更改包名
- if 'packageName' in config and config['packageName'] != '':
- ret = change_package_name(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 更改app名
- if 'name' in config and config['name'] != '':
- ret = change_app_name(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 更改app icon
- if config['changeIcon']:
- ret = change_app_icon(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 添加启动图操作
- if config['addLauncher']:
- ret = add_launcher(game, sdk, sub_channel, config)
- if ret:
- return ret
- copy_icon(game, sdk, sub_channel, config)
- # 打包lib依赖
- ret = pack_jar(game, sdk, sub_channel, config)
- if ret:
- return ret
- # sdk脚本处理
- ret = do_sdk_post_script(game, sdk, config)
- if ret:
- return ret
- # 游戏脚本处理
- ret = do_game_post_script(game, sdk, config)
- if ret:
- return ret
- # 游戏独立处理
- ret = do_game_script(game, sdk, config)
- if ret:
- return ret
- # log sdk
- if 'logSdk' in config:
- for log in config['logSdk']:
- ret = add_log_sdk(game, sdk, sub_channel, config, log)
- if ret:
- return ret
- # 生成R文件
- '''ret = generateNewRFile(game, sdk, subChannel, config)
- if ret:
- return ret'''
- # 添加MultiDex支持
- if config['splitDex']:
- print('重构sdk split smali')
- decompile_path = file_utils.get_decompile_path(game, sdk, sub_channel, config['cache'])
- manifest = os.path.join(decompile_path, 'AndroidManifest.xml')
- application = xml_utils.get_application_attr(manifest, 'name')
- ret = split_smali(game, sdk, sub_channel, config, application)
- if ret:
- return ret
- # 更改版本号
- ret = changeVersion(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 更改HardwareAccelerated属性
- replace_hardware_accelerated(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 回编译
- ret = recomplie(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 对齐apk
- ret = alignApk(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 签名
- ret = apk_signer_apk(game, sdk, sub_channel, config)
- if ret:
- return ret
- copy_v2_apk_2_out_dir(game, sdk, sub_channel, config)
- if ret:
- return ret
- # 清理产生的中间文件
- if config['clearCache']:
- clearTemp(game, sdk, sub_channel, 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, sub_channel, config):
- """
- 解包
- """
- apktoolPath = file_utils.get_apktool_path()
- cacheGameApk = file_utils.get_cache_game_apk(game, config['random'], sdk)
- decompliePath = file_utils.get_decompile_path(game, sdk, sub_channel, config['cache'])
- if os.path.exists(decompliePath):
- print('delete decomplie folder...')
- file_utils.delete_folder(decompliePath)
- print('decomplie apk...')
- return file_utils.exec_jar_cmd(apktoolPath, 'd -f "%s" -o "%s"' % (cacheGameApk, decompliePath))
- def remove_old_code(game, sdk, sub_channel, config):
- """
- 删除旧代码
- """
- print(config)
- if config['refactorSdk']:
- return handle_qingshi_old_code(game, sdk, sub_channel, config)
- else:
- return handle_jm_old_code(game, sdk, sub_channel, config)
- def handle_jm_old_code(game, sdk, sub_channel, config):
- decompliePath = file_utils.get_decompile_path(game, sdk, sub_channel, 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)
- return 0
- def handle_qingshi_old_code(game, sdk, sub_channel, config):
- print('handle_qingshi_old_code')
- decompile_path = file_utils.get_decompile_path(game, sdk, sub_channel, config['cache'])
- qingshi_path = os.path.join(decompile_path, 'smali', 'cn', 'qingshi')
- support_path = os.path.join(decompile_path, 'smali', 'cn', 'yyxx')
- xi_path = os.path.join(decompile_path, 'smali', 'XI')
- ali_path = os.path.join(decompile_path, 'smali', 'com', 'alibaba')
- asus_path = os.path.join(decompile_path, 'smali', 'com', 'asus')
- bun_path = os.path.join(decompile_path, 'smali', 'com', 'bun')
- cmic_path = os.path.join(decompile_path, 'smali', 'com', 'cmic')
- dolin_path = os.path.join(decompile_path, 'smali', 'com', 'dolin')
- huawei_path = os.path.join(decompile_path, 'smali', 'com', 'huawei')
- mobile_path = os.path.join(decompile_path, 'smali', 'com', 'mobile')
- netease_path = os.path.join(decompile_path, 'smali', 'com', 'netease')
- nirvana_path = os.path.join(decompile_path, 'smali', 'com', 'nirvana')
- samsung_path = os.path.join(decompile_path, 'smali', 'com', 'samsung')
- mmkv_path = os.path.join(decompile_path, 'smali', 'com', 'tencent', 'mmkv')
- zui_path = os.path.join(decompile_path, 'smali', 'com', 'zui')
- annotation_path = os.path.join(decompile_path, 'smali', 'android', 'support', 'annotation')
- v4_path = os.path.join(decompile_path, 'smali', 'android', 'support', 'v4')
- file_utils.delete_folder(qingshi_path)
- file_utils.delete_folder(support_path)
- file_utils.delete_folder(xi_path)
- file_utils.delete_folder(ali_path)
- file_utils.delete_folder(asus_path)
- file_utils.delete_folder(bun_path)
- file_utils.delete_folder(cmic_path)
- file_utils.delete_folder(dolin_path)
- file_utils.delete_folder(huawei_path)
- file_utils.delete_folder(mobile_path)
- file_utils.delete_folder(netease_path)
- file_utils.delete_folder(nirvana_path)
- file_utils.delete_folder(samsung_path)
- file_utils.delete_folder(mmkv_path)
- file_utils.delete_folder(zui_path)
- file_utils.delete_folder(qingshi_path)
- file_utils.delete_folder(annotation_path)
- file_utils.delete_folder(v4_path)
- return 0
- def copy_res(game, sdk, sub_channel, config):
- """
- 复制res资源
- """
- # 拷贝sdk资源
- print('copy res...')
- decompliePath = file_utils.get_decompile_path(game, sdk, sub_channel, config['cache'])
- sdkPath = file_utils.get_full_sdk_path(sdk)
- resPath = os.path.join(sdkPath, 'res')
- decomplieResPath = file_utils.get_full_path(decompliePath, 'res')
- for d in os.listdir(resPath):
- copy_res_with_type(resPath, decomplieResPath, d)
- # 拷贝assets
- print('copy assets...')
- assetsPath = file_utils.get_full_path(sdkPath, 'assets')
- decomplieAssetsPath = file_utils.get_full_path(decompliePath, 'assets')
- if os.path.exists(assetsPath):
- ret = file_utils.copy_file_all_dir(assetsPath, decomplieAssetsPath)
- if ret:
- return ret
- # 拷贝jniLib
- print('copy jniLibs...')
- jniPath = file_utils.get_full_path(sdkPath, 'jniLibs')
- decomplieJniPath = file_utils.get_full_path(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.copy_file_all_dir(
- jniPath, decomplieJniPath, False, abiFilters)
- if ret:
- return ret
- return 0
- def merge_drawable_res(game, sdk, sub_channel, config):
- """
- 合并Drawable-v4目录
- """
- print('merge drawable path...')
- decompliePath = file_utils.get_decompile_path(
- game, sdk, sub_channel, 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.copy_file_all_dir(
- v4DrawablePath, drawablePath, True)
- if ret:
- return ret
- else:
- os.rename(v4DrawablePath, drawablePath)
- return 0
- def remove_v2_old_res(game, sdk, sub_channel, config):
- print('remove v2 sdk old res...')
- decompile_path = file_utils.get_decompile_path(game, sdk, sub_channel, config['cache'])
- res_path = os.path.join(decompile_path, 'res')
- sub_folders = os.listdir(res_path)
- for folder in sub_folders:
- sub_folder_path = os.path.join(res_path, folder)
- qs_xml_docs = os.listdir(sub_folder_path)
- for xml in qs_xml_docs:
- if xml.startswith('qs_') or xml.startswith('authsdk_'):
- xml_path = os.path.join(sub_folder_path, xml)
- print(xml_path)
- os.remove(xml_path)
- if xml == 'widget_pns_action_bar.xml' or xml == 'widget_pns_optional_viewgroup.xml' or xml == 'widget_pns_protocol.xml':
- xml_path = os.path.join(sub_folder_path, xml)
- print(xml_path)
- os.remove(xml_path)
- def remove_same_values_res(game, sdk, sub_channel, config):
- """
- 移除相同的资源
- """
- # 读取sdk的资源
- print('remove same res...')
- sdk_path = file_utils.get_full_sdk_path(sdk)
- sdk_res_path = os.path.join(sdk_path, 'res')
- print('sdk res path: %s' % sdk_res_path)
- res_list = []
- for path in os.listdir(sdk_res_path):
- print('path: %s' % path)
- if not path.startswith('values'):
- continue
- abs_path = os.path.join(sdk_res_path, path)
- print('abs path: %s' % abs_path)
- for res_file in os.listdir(abs_path):
- if res_file.endswith('.DS_Store'):
- continue
- res_list = xml_utils.read_all_res(os.path.join(abs_path, res_file), res_list)
- if len(res_list) == 0:
- print('no same res found')
- return 0
- # 移除相同的资源
- decompile_path = file_utils.get_decompile_path(game, sdk, sub_channel, config['cache'])
- resPath = os.path.join(decompile_path, 'res')
- for path in os.listdir(resPath):
- if not path.startswith('values'):
- continue
- abs_path = os.path.join(resPath, path)
- for res_file in os.listdir(abs_path):
- xml_utils.remove_same_values_res(os.path.join(abs_path, res_file), res_list)
- return 0
- def merge_manifest_res(game, sdk, sub_channel, config):
- """
- 合并主文件
- """
- print('merge AndroidManifest...')
- decompliePath = file_utils.get_decompile_path(game, sdk, sub_channel, config['cache'])
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
- sdkPath = file_utils.get_full_sdk_path(sdk)
- libManifest = file_utils.get_full_path(sdkPath, 'manifest.xml')
- return xml_utils.merge_manifest_res(manifest, libManifest)
- def copy_app_res(game, sdk, sub_channel, config):
- """
- 拷贝app的资源,比如app icon、启动图等
- """
- random = config['random']
- channelPath = file_utils.getSubChannelPath(game, sdk, random, sub_channel)
- decompliePath = file_utils.get_decompile_path(
- game, sdk, sub_channel, config['cache'])
- # assets
- print('copy assets...')
- # 适配一剑斩仙
- if game in adaptApp:
- print('适配一剑斩仙...')
- decomplieAssetsPath = file_utils.get_full_path(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.get_full_path(channelPath, 'assets')
- decomplieAssetsPath = file_utils.get_full_path(
- decompliePath, 'assets')
- if os.path.exists(assetsPath):
- ret = file_utils.copy_file_all_dir(
- 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.get_full_path(channelPath, 'assets')
- decomplieAssetsPath = file_utils.get_full_path(
- decompliePath, 'assets')
- if os.path.exists(assetsPath):
- ret = file_utils.copy_file_all_dir(
- assetsPath, decomplieAssetsPath)
- if ret:
- return ret
- else:
- print('normal copy assets...')
- assetsPath = file_utils.get_full_path(channelPath, 'assets')
- decomplieAssetsPath = file_utils.get_full_path(decompliePath, 'assets')
- if os.path.exists(assetsPath):
- ret = file_utils.copy_file_all_dir(assetsPath, decomplieAssetsPath)
- if ret:
- return ret
- # icon
- print('copy icon...')
- ret = copy_app_res_with_type(decompliePath, channelPath, 'icon')
- if ret:
- return ret
- # 启动图
- print('copy splash...')
- ret = copy_app_res_with_type(decompliePath, channelPath, 'splash')
- if ret:
- return ret
- # 其他图片
- print('copy image...')
- ret = copy_app_res_with_type(decompliePath, channelPath, 'image')
- if ret:
- return ret
- return 0
- def copy_app_res_with_type(decompile_path, channel_path, type_name):
- decomplieResPath = os.path.join(decompile_path, 'res')
- iconPath = os.path.join(channel_path, type_name)
- if not os.path.exists(iconPath):
- print('dir "%s" not exists' % iconPath)
- return 0
- for d in os.listdir(iconPath):
- ret = copy_res_with_type(iconPath, decomplieResPath, d)
- if ret:
- return ret
- return 0
- def copy_res_with_type(res_path, decompile_res_path, type_name):
- # aapt的打包目录会带-v4后缀
- resDir = os.path.join(res_path, type_name)
- target = os.path.join(decompile_res_path, type_name)
- targetV4 = os.path.join(decompile_res_path, type_name + '-v4')
- if not os.path.exists(target) and not os.path.exists(targetV4):
- os.makedirs(target)
- return file_utils.copy_file_all_dir(resDir, target, False)
- elif not os.path.exists(target):
- return file_utils.copy_file_all_dir(resDir, targetV4, False)
- else:
- return file_utils.copy_file_all_dir(resDir, target, False)
- def remove_no_support_attr(game, sdk, sub_channel, config):
- """
- 删除一些不支持的属性
- """
- decompile_path = file_utils.get_decompile_path(game, sdk, sub_channel, config['cache'])
- manifest = os.path.join(decompile_path, 'AndroidManifest.xml')
- xml_utils.remove_root_attr(manifest, 'compileSdkVersion')
- xml_utils.remove_root_attr(manifest, 'compileSdkVersionCodename')
- return 0
- def fix_un_support_config(game, sdk, sub_channel, config):
- """
- 删除一些不支持的配置
- """
- # 检查minSdkVersion
- decompile_path = file_utils.get_decompile_path(game, sdk, sub_channel, config['cache'])
- yml = os.path.join(decompile_path, 'apktool.yml')
- min_sdk_version = 21
- file_utils.change_min_sdk_version(yml, min_sdk_version)
- res_path = os.path.join(decompile_path, 'res')
- tag = '-v'
- for res in os.listdir(res_path):
- # 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 < min_sdk_version:
- un_support_path = os.path.join(res_path, res)
- print('un_support_path = ' + un_support_path)
- file_utils.delete_folder(un_support_path)
- print('deleteFolder = ' + un_support_path)
- return 0
- def change_package_name(game, sdk, sub_channel, config):
- """
- 更改包名
- """
- # 全局替换AndroidManifest里面的包名
- newPackageName = config['packageName']
- decompliePath = file_utils.get_decompile_path(game, sdk, sub_channel, config['cache'])
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
- packageName = xml_utils.get_package_name(manifest)
- config['oldPackageName'] = packageName
- xml_utils.change_package_name(manifest, newPackageName)
- print('change package name %s --> %s' % (packageName, newPackageName))
- return 0
- def change_app_name(game, sdk, sub_channel, config):
- """
- 更改app名
- """
- # 生成string.xml文件
- name = config['name']
- resName = 'common_sdk_name'
- if 'outName' in config:
- resName = resName + '_' + config['outName']
- decompliePath = file_utils.get_decompile_path(
- game, sdk, sub_channel, 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.create_file(stringFile, content)
- # 修改主文件的app名的值
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
- xml_utils.change_app_name(manifest, '@string/%s' % resName)
- print('change app name %s' % name)
- return 0
- def change_app_icon(game, sdk, subChannel, config):
- '''
- 更改app icon
- '''
- print('change app icon...')
- resName = 'common_sdk_icon'
- decompliePath = file_utils.get_decompile_path(
- game, sdk, subChannel, config['cache'])
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
- xml_utils.change_app_icon(manifest, '@mipmap/%s' % resName)
- return 0
- def add_meta_data(game, sdk, subChannel, config):
- '''
- 添加meta-data
- '''
- if 'metaData' not in config:
- return 0
- print('add meta-data...')
- decompliePath = file_utils.get_decompile_path(
- game, sdk, subChannel, config['cache'])
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
- xml_utils.addMetaData(manifest, config['metaData'])
- return 0
- def add_v1_config(game, sdk, sub_channel, config):
- """
- 添加config.json
- """
- if 'configData' not in config:
- return 0
- print('add config.json...')
- decompile_path = file_utils.get_decompile_path(game, sdk, sub_channel, config['cache'])
- configJson = os.path.join(decompile_path, 'assets', 'jmhy_config.json')
- jsonText = json.dumps(config['configData'], ensure_ascii=False)
- file_utils.create_file(configJson, jsonText)
- return 0
- def add_v2_config(game, sdk, sub_channel, config):
- """
- 添加config.json
- """
- if 'configData' not in config:
- return 0
- print('add config.json...')
- decompile_path = file_utils.get_decompile_path(game, sdk, sub_channel, config['cache'])
- config_json = os.path.join(decompile_path, 'assets', 'qs_game', 'qs_analytics.json')
- if os.path.exists(config_json):
- os.remove(config_json)
- json_text = json.dumps(config['configData']['channel_sdk_list'], ensure_ascii=False)
- file_utils.create_file(config_json, json_text)
- return 0
- def change_placeholders(game, sdk, sub_channel, config):
- """
- 处理掉占位符
- """
- if 'placeholders' not in config:
- return 0
- decompliePath = file_utils.get_decompile_path(game, sdk, sub_channel, 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.replace_content(manifest, oldText, newText)
- return 0
- def add_launcher(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.get_decompile_path(
- game, sdk, subChannel, config['cache'])
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
- activity = xml_utils.get_launcher_activity_name(manifest)
- if activity == 'com.jmhy.sdk.template.LauncherActivity':
- print('add launcher already exist...')
- return 1
- # 添加关联资源
- internalPath = file_utils.getFullInternalPath()
- ret = copy_app_res_with_type(decompliePath, internalPath, 'launcher_res')
- if ret:
- return ret
- # 拷贝代码
- print('copy launcher code...')
- codePath = os.path.join(internalPath, 'launcher_code', 'smali')
- smaliPath = file_utils.get_full_path(decompliePath, 'smali')
- ret = file_utils.copy_file_all_dir(codePath, smaliPath)
- if ret:
- return ret
- # 修改主文件信息
- print('change launcher config...')
- orientation = xml_utils.get_screen_orientation(manifest)
- activity = xml_utils.remove_launcher_activity(manifest)
- xml_utils.add_launcher_activity(
- manifest, orientation, 'com.jmhy.sdk.template.LauncherActivity')
- # 修改跳转的
- launcherActivity = os.path.join(
- decompliePath, 'smali', 'com', 'jmhy', 'sdk', 'template', 'LauncherActivity.smali')
- file_utils.replace_content(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.replace_content(launcherActivity, '0x0BB8', timeHex)
- return 0
- def addMoreIcon(game, sdk, subChannel, config):
- '''
- 添加多个图标
- '''
- print('add more icon support...')
- decompliePath = file_utils.get_decompile_path(
- game, sdk, subChannel, config['cache'])
- icon = '@mipmap/common_sdk_icon'
- if not config['changeIcon']:
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
- icon = xml_utils.get_application_attr(manifest, 'icon')
- switchIcon = icon
- if config['switchIcon']:
- switchIcon = '@mipmap/common_sdk_icon2'
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
- return xml_utils.add_more_icon(manifest, icon, switchIcon)
- def copy_icon(game, sdk, subChannel, config):
- '''
- 复制icon到res ,一键登录使用
- '''
- if config['changeIcon']:
- decompliePath = file_utils.get_decompile_path(game, sdk, subChannel, config['cache'])
- decomplieResPath = file_utils.get_full_path(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.copy_file(iconPath, desPath)
- else:
- decompliePath = file_utils.get_decompile_path(game, sdk, subChannel, config['cache'])
- decomplieResPath = file_utils.get_full_path(decompliePath, 'res')
- desPath = os.path.join(decomplieResPath, 'drawable-hdpi', 'jm_cmcc_icon.png')
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
- icon = xml_utils.get_application_attr(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.copy_file(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 do_sdk_post_script(game, sdk, config):
- """
- 执行sdk相关特殊处理脚本
- """
- sdkPath = file_utils.get_full_sdk_path(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('do sdk post script...')
- sys.path.append(scriptPath)
- module = importlib.import_module('sdk_script')
- ret = module.execute(game, sdk, config)
- sys.path.remove(scriptPath)
- return ret
- def do_game_post_script(game, sdk, config):
- """
- 执行游戏相关特殊处理脚本
- """
- channelPath = file_utils.get_full_game_path(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 do_game_script(game, sdk, config):
- """
- 执行游戏相关特殊处理脚本
- """
- channelPath = file_utils.get_full_path('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 add_log_sdk(game, sdk, sub_channel, config, logSdk):
- # 拷贝jniLibs
- decompliePath = file_utils.get_decompile_path(game, sdk, sub_channel, config['cache'])
- print('add log sdk, sdk:' + sdk)
- if sdk == 'jm_beta_sdk' or sdk == 'beta_sdk':
- sdk_path = file_utils.get_full_log_sdk_path(logSdk, True)
- elif sdk == 'qingshi':
- sdk_path = file_utils.get_full_log_sdk_v2_path(logSdk)
- else:
- sdk_path = file_utils.get_full_log_sdk_path(logSdk)
- print('copy log jniLibs...')
- jni_path = file_utils.get_full_path(sdk_path, 'jniLibs')
- decomplie_jni_path = file_utils.get_full_path(decompliePath, 'lib')
- abiFilters = []
- if os.path.exists(decomplie_jni_path):
- for abi in os.listdir(decomplie_jni_path):
- if abi == 'armeabi-v7a' or abi == 'x86' or abi == 'x86_64' or abi == 'arm64-v8a':
- abiFilters.append(abi)
- else:
- abiFilters = ['armeabi-v7a']
- if os.path.exists(jni_path):
- ret = file_utils.copy_file_all_dir(jni_path, decomplie_jni_path, False, abiFilters)
- if ret:
- return ret
- print('merge log AndroidManifest...')
- libManifest = file_utils.get_full_path(sdk_path, 'manifest.xml')
- if os.path.exists(libManifest):
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
- ret = xml_utils.merge_manifest_res(manifest, libManifest)
- if ret:
- return ret
- change_placeholders(game, sdk, sub_channel, config)
- return pack_log_jar(game, sdk, sub_channel, config, logSdk)
- def addOaidSdk(game, sdk, subChannel, config, oaidVersion):
- sdkPath = file_utils.getFullOaidSDKPath(oaidVersion)
- decompliePath = file_utils.get_decompile_path(
- game, sdk, subChannel, config['cache'])
- # 拷贝assets
- print('copy oaid assets...')
- assetsPath = file_utils.get_full_path(sdkPath, 'assets')
- decomplieAssetsPath = file_utils.get_full_path(decompliePath, 'assets')
- if os.path.exists(assetsPath):
- ret = file_utils.copy_file_all_dir(assetsPath, decomplieAssetsPath)
- if ret:
- return ret
- # 拷贝jniLibs
- print('copy oaid jniLibs...')
- jniPath = file_utils.get_full_path(sdkPath, 'jniLibs')
- decomplieJniPath = file_utils.get_full_path(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.copy_file_all_dir(
- jniPath, decomplieJniPath, False, abiFilters)
- if ret:
- return ret
- print('merge oaid AndroidManifest...')
- libManifest = file_utils.get_full_path(sdkPath, 'manifest.xml')
- if os.path.exists(libManifest):
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
- ret = xml_utils.merge_manifest_res(manifest, libManifest)
- if ret:
- return ret
- return packOaidJar(game, sdk, subChannel, config, oaidVersion)
- def generateNewRFile(game, sdk, subChannel, config):
- """
- 生成新的R文件
- """
- decompliePath = file_utils.get_decompile_path(
- game, sdk, subChannel, config['cache'])
- androidPlatforms = file_utils.get_android_compile_tool_path()
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
- decomplieResPath = file_utils.get_full_path(decompliePath, 'res')
- compliePath = file_utils.get_full_path(decompliePath, 'gen')
- if not os.path.exists(compliePath):
- os.makedirs(compliePath)
- # 生成R文件
- print('生成R文件 ...')
- if config['aapt2disable']:
- aapt = file_utils.get_aapt_path()
- ret = file_utils.get_exec_permission(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.exec_format_cmd(createRCmd)
- if ret:
- return ret
- else:
- # compile
- aapt = file_utils.get_aapt2_path()
- ret = file_utils.get_exec_permission(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.exec_format_cmd(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.exec_format_cmd(linkResCmd, unzipResPath)
- if ret:
- return ret
- # 编译R文件
- print('complie R.java ...')
- packageName = xml_utils.get_package_name(manifest)
- packagePath = file_utils.get_package_path(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.exec_format_cmd(complieRCmd)
- if ret:
- return ret
- # 生成dex
- print('dex R.class ...')
- outDex = os.path.join(compliePath, 'classes.dex')
- if config['aapt2disable']:
- dx = file_utils.get_dx_path()
- dexCmd = '--dex --no-warning --output="%s" "%s"' % (
- outDex, compliePath)
- else:
- dx = file_utils.get_d8_path()
- clazz = os.path.join(packagePath, '*.class')
- dexCmd = '--lib "%s" --output "%s" %s' % (
- androidPlatforms, compliePath, clazz)
- ret = file_utils.exec_jar_cmd(dx, dexCmd)
- if ret:
- return ret
- # 反向dex生成smali
- # 存放在out目录
- print('baksmali classes.dex ...')
- baksmaliPath = file_utils.get_baksmali_path()
- outPath = file_utils.get_full_path(decompliePath, 'out')
- ret = file_utils.exec_jar_cmd(
- baksmaliPath, 'd "%s" -o "%s"' % (outDex, outPath))
- if ret:
- return ret
- # 将生成的文件拷贝到目标目录
- print('copy R.smali ...')
- smaliPath = file_utils.get_full_path(decompliePath, 'smali')
- file_utils.copy_file_all_dir(outPath, smaliPath)
- return 0
- def pack_jar(game, sdk, sub_channel, config):
- """
- 打包所有的jar
- """
- split_dex = config['splitDex']
- decompile_path = file_utils.get_decompile_path(
- game, sdk, sub_channel, config['cache'])
- outPath = file_utils.get_full_path(decompile_path, 'gen')
- if not os.path.exists(outPath):
- os.makedirs(outPath)
- if config['aapt2disable']:
- dx = file_utils.get_dx_path()
- dex_cmd = '--dex --no-warning --output="%s"' % outPath
- else:
- dx = file_utils.get_d8_path()
- androidPlatforms = file_utils.get_android_compile_tool_path()
- dex_cmd = '--lib "%s" --output "%s"' % (androidPlatforms, outPath)
- # 找到所有lib依赖
- sdk_path = file_utils.get_full_sdk_path(sdk)
- libs = os.path.join(sdk_path, 'libs')
- libConfig = os.path.join(libs, 'config.json')
- # 存在配置文件
- if os.path.exists(libConfig):
- jsonText = file_utils.read_file(libConfig)
- libConf = json.loads(jsonText)
- if 'libConfig' in config and config['libConfig'] in libConf:
- conf = config['libConfig']
- libList = libConf[conf]
- for jar in libList:
- dex_cmd += ' ' + os.path.join(libs, jar)
- elif 'default' in libConf:
- libList = libConf['default']
- for jar in libList:
- dex_cmd += ' ' + os.path.join(libs, jar)
- else:
- for jar in os.listdir(libs):
- if not jar.endswith('.jar'):
- continue
- dex_cmd += ' ' + os.path.join(libs, jar)
- else:
- for jar in os.listdir(libs):
- if not jar.endswith('.jar'):
- continue
- dex_cmd += ' ' + os.path.join(libs, jar)
- if 'refactorSdk' in config and config['refactorSdk']:
- pass
- else:
- # multidex.jar
- if split_dex:
- print('getMultiDexPath ...')
- dex_cmd += ' ' + file_utils.get_multidex_path()
- # sdk实现类
- dex_cmd += ' ' + os.path.join(sdk_path, '%s.jar' % sdk)
- print('packaging all jar ...')
- ret = file_utils.exec_jar_cmd(dx, dex_cmd)
- if ret:
- return ret
- # 反向dex生成smali
- # 存放在out目录
- print('baksmali classes.dex ...')
- outDex = os.path.join(outPath, 'classes.dex')
- baksmaliPath = file_utils.get_baksmali_path()
- outPath = file_utils.get_full_path(decompile_path, 'out')
- ret = file_utils.exec_jar_cmd(baksmaliPath, 'd "%s" -o "%s"' % (outDex, outPath))
- if ret:
- return ret
- # 将生成的文件拷贝到目标目录
- print('copy all smali ...')
- smaliPath = file_utils.get_full_path(decompile_path, 'smali')
- ret = file_utils.copy_file_all_dir(outPath, smaliPath, True)
- if ret:
- return ret
- return 0
- def pack_log_jar(game, sdk, sub_channel, config, log_sdk):
- """
- 打包Log jar
- """
- decompliePath = file_utils.get_decompile_path(game, sdk, sub_channel, config['cache'])
- outPath = file_utils.get_full_path(decompliePath, 'gen')
- if not os.path.exists(outPath):
- os.makedirs(outPath)
- if config['aapt2disable']:
- dx = file_utils.get_dx_path()
- dexCmd = '--dex --multi-dex --no-warning --output="%s"' % outPath
- else:
- dx = file_utils.get_d8_path()
- androidPlatforms = file_utils.get_android_compile_tool_path()
- dexCmd = '--lib "%s" --output "%s"' % (androidPlatforms, outPath)
- # 找到所有lib依赖
- if sdk == 'jm_beta_sdk' or sdk == 'beta_sdk':
- sdk_path = file_utils.get_full_log_sdk_path(log_sdk, True)
- elif sdk == 'qingshi':
- sdk_path = file_utils.get_full_log_sdk_v2_path(log_sdk)
- else:
- sdk_path = file_utils.get_full_log_sdk_path(log_sdk)
- libs = os.path.join(sdk_path, 'libs')
- lib_config = os.path.join(libs, 'config.json')
- # 存在配置文件
- if os.path.exists(lib_config):
- json_text = file_utils.read_file(lib_config)
- lib_list = json.loads(json_text)
- for jar in lib_list:
- 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('packaging all log jar ...')
- if sdk != 'qingshi':
- dexCmd += ' ' + os.path.join(sdk_path, '%s.jar' % log_sdk)
- ret = file_utils.exec_jar_cmd(dx, dexCmd)
- if ret:
- return ret
- # 反向dex生成smali
- # 存放在out目录
- print('baksmali classes.dex ...')
- outDex = os.path.join(outPath, 'classes.dex')
- baksmaliPath = file_utils.get_baksmali_path()
- outPath = file_utils.get_full_path(decompliePath, 'out')
- ret = file_utils.exec_jar_cmd(
- baksmaliPath, 'd "%s" -o "%s"' % (outDex, outPath))
- if ret:
- return ret
- # 将生成的文件拷贝到目标目录
- print('copy all log smali ...')
- smaliPath = file_utils.get_full_path(decompliePath, 'smali')
- ret = file_utils.copy_file_all_dir(outPath, smaliPath, True)
- if ret:
- return ret
- return 0
- def packOaidJar(game, sdk, subChannel, config, oaidVerion):
- '''
- 打包oaid jar
- '''
- decompliePath = file_utils.get_decompile_path(
- game, sdk, subChannel, config['cache'])
- outPath = file_utils.get_full_path(decompliePath, 'gen')
- if not os.path.exists(outPath):
- os.makedirs(outPath)
- if config['aapt2disable']:
- dx = file_utils.get_dx_path()
- dexCmd = '--dex --multi-dex --no-warning --output="%s"' % outPath
- else:
- dx = file_utils.get_d8_path()
- androidPlatforms = file_utils.get_android_compile_tool_path()
- 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.exec_jar_cmd(dx, dexCmd)
- if ret:
- return ret
- # 反向dex生成smali
- # 存放在out目录
- print('baksmali classes.dex ...')
- outDex = os.path.join(outPath, 'classes.dex')
- baksmaliPath = file_utils.get_baksmali_path()
- outPath = file_utils.get_full_path(decompliePath, 'out')
- ret = file_utils.exec_jar_cmd(
- baksmaliPath, 'd "%s" -o "%s"' % (outDex, outPath))
- if ret:
- return ret
- # 将生成的文件拷贝到目标目录
- print('copy all log smali ...')
- smaliPath = file_utils.get_full_path(decompliePath, 'smali')
- ret = file_utils.copy_file_all_dir(outPath, smaliPath, True)
- if ret:
- return ret
- return 0
- def split_dex(game, sdk, sub_channel, config):
- """
- 分割dex
- """
- # 判断是否已经存在application
- # 存在,则往原application添加内容
- # 不存在,则拷贝一个默认的android.support.multidex.MultiDexApplication
- print('add MultiDex support...')
- decompile_path = file_utils.get_decompile_path(game, sdk, sub_channel, config['cache'])
- manifest = os.path.join(decompile_path, 'AndroidManifest.xml')
- application = xml_utils.get_application_attr(manifest, 'name')
- if application is None:
- ret = xml_utils.change_application_attr(manifest, 'name', 'android.support.multidex.MultiDexApplication')
- if ret:
- return ret
- else:
- smaliPath = os.path.join(decompile_path, 'smali')
- application_file = file_utils.get_package_path(smaliPath, application)
- application_file += '.smali'
- ret = change_application_dex(application_file)
- if ret:
- return ret
- return split_smali(game, sdk, sub_channel, config, application)
- def change_application_dex(file):
- """
- 修改application的smali文件,增加MultiDex操作
- """
- index = file_utils.get_application_smali_index(file)
- file_utils.insert_application_smali(file, index)
- return 0
- def split_smali(game, sdk, sub_channel, config, application):
- """
- 如果函数上限超过限制,自动拆分smali,以便生成多个dex文件
- """
- print('splitSmali...')
- decompliePath = file_utils.get_decompile_path(game, sdk, sub_channel, 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.copy_file(f, targetFile, True)
- return 0
- def changeVersion(game, sdk, sub_channel, config):
- """
- 更改版本号
- """
- decompliePath = file_utils.get_decompile_path(
- game, sdk, sub_channel, 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.get_apktool_path()
- decompliePath = file_utils.get_decompile_path(
- game, sdk, subChannel, config['cache'])
- outApk = file_utils.getOutApkPath(game, sdk, subChannel, config['cache'])
- useAppt2 = ' --use-aapt2'
- if config['aapt2disable']:
- useAppt2 = ''
- return file_utils.exec_jar_cmd(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.get_zipalign_path()
- if os.path.exists(alignApk):
- os.remove(alignApk)
- ret = file_utils.get_exec_permission(alignapkTool)
- if ret:
- return ret
- # zipalign.exe -v -p 4 input.apk output.apk
- return file_utils.exec_format_cmd('"%s" -f -p 4 "%s" "%s"' % (alignapkTool, outApk, alignApk))
- def apk_signer_apk(game, sdk, sub_channel, config):
- """
- 签名apk
- """
- print('sign apk...')
- print('game = %s, sdk = %s, subChannel = %s, ...' %
- (game, sdk, sub_channel))
- path = os.path.join(file_utils.get_current_path(), 'keystore', 'key.json')
- jsonText = file_utils.read_file(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.get_apksigner_path()
- alignApk = file_utils.getAlignApkPath(
- game, sdk, sub_channel, config['cache'])
- signedApk = file_utils.get_signed_apk_path(
- game, sdk, sub_channel, config['cache'])
- storeFile = os.path.join(file_utils.get_current_path(),
- '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.exec_jar_cmd(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 add_channel(game, sdk, sub_channel, config):
- """
- 添加渠道信息
- """
- if 'v2disable' in config and config['v2disable']:
- return 0
- walle = file_utils.getWallePath()
- signedApk = file_utils.get_signed_apk_path(game, sdk, sub_channel, config['cache'])
- walleApk = file_utils.getWalleApkPath(game, sdk, sub_channel, 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.exec_jar_cmd(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, sub_channel, config):
- """
- 清空中间产生的文件
- """
- print('clear temp...')
- targetApkPath = file_utils.getTargetApkPath(game, sdk, config['cache'])
- if os.path.exists(targetApkPath):
- file_utils.delete_folder(targetApkPath)
- decompliePath = file_utils.get_decompile_path(
- game, sdk, sub_channel, config['cache'])
- if os.path.exists(decompliePath):
- file_utils.delete_folder(decompliePath)
- random = config['random']
- channelPath = file_utils.getChannelPath(game, random, sdk)
- if os.path.exists(channelPath):
- file_utils.delete_folder(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.get_cache_game_apk(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.get_full_sdk_path(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.read_file(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.delete_folder(subChannelPath)
- return 0
- def formatXml(game, sdk, subChannel, config):
- decompliePath = file_utils.get_decompile_path(
- game, sdk, subChannel, config['cache'])
- manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
- return xml_utils.formatXml(manifest)
- def copy_v1_apk_2_out_dir(game, sdk, sub_channel, config):
- walleApk = file_utils.getWalleApkPath(game, sdk, sub_channel, 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.copy_file(walleApk, releaseApkPath)
- def copy_v2_apk_2_out_dir(game, sdk, sub_channel, config):
- signed_apk = file_utils.get_signed_apk_path(game, sdk, sub_channel, config['cache'])
- release_apk_path = ""
- if 'outName' in config and 'outPath' in config:
- if not os.path.exists(config['outPath']):
- os.makedirs(config['outPath'])
- release_apk_path = os.path.join(config['outPath'], config['outName'] + '.apk')
- elif 'outName' in config:
- release_apk_path = file_utils.getRenameApkPath(game, sdk, config['cache'], config['outName'])
- print('------生成正式包路径 %s -------' % release_apk_path)
- file_utils.copy_file(signed_apk, release_apk_path)
- def replace_hardware_accelerated(game, sdk, subChannel, config):
- decompliePath = file_utils.get_decompile_path(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 remove_v2_old_abi(game, sdk, sub_channel, config):
- print('remove v2 old abi...')
- decompile_path = file_utils.get_decompile_path(game, sdk, sub_channel, config['cache'])
- lib_path = os.path.join(decompile_path, 'lib')
- abi_folders = os.listdir(lib_path)
- for abi_folder in abi_folders:
- abi_folder_path = os.path.join(lib_path, abi_folder)
- abi_files = os.listdir(abi_folder_path)
- for abi in abi_files:
- if abi == 'libalicomphonenumberauthsdk_core.so':
- abi_path = os.path.join(abi_folder_path, abi)
- print(abi_path)
- os.remove(abi_path)
- if abi == 'libauth_number_product-2.12.1-log-online-standard-release_alijtca_plus.so':
- abi_path = os.path.join(abi_folder_path, abi)
- print(abi_path)
- os.remove(abi_path)
- if abi == 'libdolin-zap.so':
- abi_path = os.path.join(abi_folder_path, abi)
- print(abi_path)
- os.remove(abi_path)
- if abi == 'libmmkv.so':
- abi_path = os.path.join(abi_folder_path, abi)
- print(abi_path)
- os.remove(abi_path)
- if abi == 'libqsgamesdk.so':
- abi_path = os.path.join(abi_folder_path, abi)
- print(abi_path)
- os.remove(abi_path)
- if abi == 'libsecsdk.so':
- abi_path = os.path.join(abi_folder_path, abi)
- print(abi_path)
- os.remove(abi_path)
- def openFile(file, mode):
- return open(file, mode, encoding='UTF-8')
- def is_refactor_sdk(config) -> bool:
- return 'refactorSdk' in config and config['refactorSdk']
|