package_utils.py 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473
  1. # 安卓游戏打包脚本
  2. # 1.用apktool解包
  3. # 2.复制res资源、assets资源、jniLib资源
  4. # 3.修改包名、app名、渠道文件
  5. # 4.添加app icon、合并AndroidManifest文件
  6. # 5.添加app启动图,修改AndroidManifest文件
  7. # 6.生成新的R文件
  8. # 7.将新的R文件编译,生成dex,再用baksmali生成smali,替换旧的R文件
  9. # 8.将jar资源打包成dex,再用baksmali生成smali,拷贝到smali目录下
  10. # 9.统计方法量,并分割dex(将smali文件拷贝到目录smali_classes2)
  11. # 10.用apktool回包
  12. # 11.重新签名
  13. import file_utils
  14. import xml_utils
  15. import smali_utils
  16. import config_utils
  17. import game_utils
  18. import common_utils
  19. import os
  20. import os.path
  21. import json
  22. import sys
  23. import importlib
  24. import uuid
  25. import zipfile
  26. import time
  27. import datetime
  28. ignoreLauncher = ['jm_ysdk', 'jm_yijie', 'jm_quick', 'jm_beiyu', 'jm_xq_jrtt','jm_zy_ysdk']
  29. adaptApp = ['yjzx']
  30. startTime = ''
  31. def pack(game, sdk, config):
  32. config['cache'] = uuid.uuid1()
  33. subChannel = config['subChannel']
  34. print('game = %s, sdk = %s, subChannel = %s, ...' % (game,sdk,subChannel))
  35. # 解包
  36. ret = decomplie(game, sdk, subChannel, config)
  37. if ret:
  38. return ret
  39. # 删除旧代码
  40. ret = removeOldCode(game, sdk, subChannel, config)
  41. if ret:
  42. return ret
  43. # 删除一些不支持的属性
  44. ret = removeNoSupportAttr(game, sdk, subChannel, config)
  45. if ret:
  46. return ret
  47. # 删除一些不支持的配置
  48. ret = fixUnSupportConfig(game, sdk, subChannel, config)
  49. if ret:
  50. return ret
  51. # 合并Drawable-v4目录
  52. ret = mergeDrawableRes(game, sdk, subChannel, config)
  53. if ret:
  54. return ret
  55. # 移除相同的资源
  56. ret = removeSameRes(game, sdk, subChannel, config)
  57. if ret:
  58. return ret
  59. # 复制res资源
  60. ret = copyRes(game, sdk, subChannel, config)
  61. if ret:
  62. return ret
  63. # 合并主文件
  64. ret = mergeManifestRes(game, sdk, subChannel, config)
  65. if ret:
  66. return ret
  67. # 替换占位符
  68. ret = changePlaceholders(game, sdk, subChannel, config)
  69. if ret:
  70. return ret
  71. # 添加meta-data
  72. ret = addMetaData(game, sdk, subChannel, config)
  73. if ret:
  74. return ret
  75. # 增加配置文件
  76. ret = addConfig(game, sdk, subChannel, config)
  77. if ret:
  78. return ret
  79. # 复制app res资源
  80. ret = copyAppRes(game, sdk, subChannel, config)
  81. if ret:
  82. return ret
  83. # 更改包名
  84. if 'packageName' in config and config['packageName'] != '':
  85. ret = changePackageName(game, sdk, subChannel, config)
  86. if ret:
  87. return ret
  88. # 更改app名
  89. if 'name' in config and config['name'] != '':
  90. ret = changeAppName(game, sdk, subChannel, config)
  91. if ret:
  92. return ret
  93. # 更改app icon
  94. if config['changeIcon']:
  95. ret = changeAppIcon(game, sdk, subChannel, config)
  96. if ret:
  97. return ret
  98. # 添加启动图操作
  99. if config['addLauncher']:
  100. ret = addLauncher(game, sdk, subChannel, config)
  101. if ret:
  102. return ret
  103. # 添加多图标
  104. #ret = addMoreIcon(game, sdk, subChannel, config)
  105. # 复制icon图标到res
  106. ret = copyIcon(game, sdk, subChannel, config)
  107. #if ret:
  108. # return ret
  109. # 打包lib依赖
  110. ret = packJar(game, sdk, subChannel, config)
  111. if ret:
  112. return ret
  113. #继承JMApplication
  114. common_utils.changeApplication(game, sdk, subChannel, config, 'com.jmhy.sdk.common.JMApplication')
  115. # sdk脚本处理
  116. ret = doSDKPostScript(game, sdk, config)
  117. if ret:
  118. return ret
  119. # 乐变sdk的特殊处理
  120. ret = game_utils.sdkLebianChange(game, sdk, config)
  121. if ret:
  122. return ret
  123. # 游戏脚本处理
  124. ret = doGamePostScript(game, sdk, config)
  125. if ret:
  126. return ret
  127. # 游戏独立处理
  128. ret = doGameScript(game, sdk, config)
  129. if ret:
  130. return ret
  131. #格式化Xml
  132. # ret = formatXml(game, sdk, subChannel, config)
  133. # if ret:
  134. # return ret
  135. # log sdk
  136. if 'logSdk' in config:
  137. for log in config['logSdk']:
  138. ret = addLogSdk(game, sdk, subChannel, config, log)
  139. if ret:
  140. return ret
  141. # oaid sdk
  142. ret = addOaidSdk(game, sdk, subChannel, config, "1.0.10")
  143. if ret:
  144. return ret
  145. # 生成R文件
  146. '''ret = generateNewRFile(game, sdk, subChannel, config)
  147. if ret:
  148. return ret'''
  149. # 添加MultiDex支持
  150. if config['splitDex']:
  151. ret = splitDex(game, sdk, subChannel, config)
  152. if ret:
  153. return ret
  154. # 更改版本号
  155. ret = changeVersion(game, sdk, subChannel, config)
  156. if ret:
  157. return ret
  158. # 回编译
  159. ret = recomplie(game, sdk, subChannel, config)
  160. if ret:
  161. return ret
  162. # 对齐apk
  163. ret = alignApk(game, sdk, subChannel, config)
  164. if ret:
  165. return ret
  166. # 签名
  167. ret = apksignerApk(game, sdk, subChannel, config)
  168. if ret:
  169. return ret
  170. # 添加渠道信息
  171. ret = addChannel(game, sdk, subChannel, config)
  172. if ret:
  173. return ret
  174. # 清理产生的中间文件
  175. if config['clearCache']:
  176. clearTemp(game, sdk, subChannel, config)
  177. endTime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  178. d1 = datetime.datetime.strptime(endTime, '%Y-%m-%d %H:%M:%S')
  179. d2 = datetime.datetime.strptime(startTime, '%Y-%m-%d %H:%M:%S')
  180. d = d1 - d2
  181. print ('开始时间:' + startTime)
  182. print ('结束时间:' + endTime)
  183. print ('用时:{}'.format(config_utils.getTime(d.seconds)))
  184. return 0
  185. def decomplie(game, sdk, subChannel, config):
  186. '''
  187. 解包
  188. '''
  189. apktoolPath = file_utils.getApkToolPath()
  190. gamePath = file_utils.getFullGameApk(game)
  191. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  192. if os.path.exists(decompliePath):
  193. print('delete decomplie folder...')
  194. file_utils.deleteFolder(decompliePath)
  195. print('decomplie apk...')
  196. return file_utils.execJarCmd(apktoolPath, 'd -f "%s" -o "%s"' % (gamePath, decompliePath))
  197. def removeOldCode(game, sdk, subChannel, config):
  198. '''
  199. 删除旧代码
  200. '''
  201. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  202. codePath = os.path.join(decompliePath, 'smali', 'com', 'jmhy', 'sdk')
  203. #file_utils.deleteFolder(codePath)
  204. allFiles = []
  205. allFiles = file_utils.list_files(codePath, allFiles)
  206. for f in allFiles:
  207. fpath, fname = os.path.split(f) #分离文件名和路径
  208. if fname == 'R.smali' or fname.startswith('R$'):
  209. continue
  210. os.remove(f)
  211. #print('remove %s' % f)
  212. return 0
  213. def copyRes(game, sdk, subChannel, config):
  214. '''
  215. 复制res资源
  216. '''
  217. # 拷贝sdk资源
  218. print('copy res...')
  219. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  220. sdkPath = file_utils.getFullSDKPath(sdk)
  221. resPath = os.path.join(sdkPath, 'res')
  222. decomplieResPath = file_utils.getFullPath(decompliePath, 'res')
  223. for d in os.listdir(resPath):
  224. copyResWithType(resPath, decomplieResPath, d)
  225. # 拷贝assets
  226. print('copy assets...')
  227. assetsPath = file_utils.getFullPath(sdkPath, 'assets')
  228. decomplieAssetsPath = file_utils.getFullPath(decompliePath, 'assets')
  229. if os.path.exists(assetsPath):
  230. ret = file_utils.copyFileAllDir(assetsPath, decomplieAssetsPath)
  231. if ret:
  232. return ret
  233. # 拷贝jniLib
  234. print('copy jniLibs...')
  235. jniPath = file_utils.getFullPath(sdkPath, 'jniLibs')
  236. decomplieJniPath = file_utils.getFullPath(decompliePath, 'lib')
  237. abiFilters = []
  238. if os.path.exists(decomplieJniPath):
  239. for abi in os.listdir(decomplieJniPath):
  240. if abi == 'armeabi-v7a' or abi == 'armeabi':
  241. abiFilters.append(abi)
  242. else:
  243. abiFilters = ['armeabi-v7a']
  244. if os.path.exists(jniPath):
  245. ret = file_utils.copyFileAllDir(jniPath, decomplieJniPath, False, abiFilters)
  246. if ret:
  247. return ret
  248. return 0
  249. def mergeDrawableRes(game, sdk, subChannel, config):
  250. '''
  251. 合并Drawable-v4目录
  252. '''
  253. print('merge drawable path...')
  254. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  255. resPath = os.path.join(decompliePath, 'res')
  256. for path in os.listdir(resPath):
  257. #print('res path %s' % path)
  258. if (path.startswith('drawable') or path.startswith('mipmap')) and path.endswith('-v4'):
  259. #print('merge path %s' % path)
  260. v4DrawablePath = os.path.join(resPath, path)
  261. drawablePath = os.path.join(resPath, path[:-3])
  262. if os.path.exists(drawablePath):
  263. ret = file_utils.copyFileAllDir(v4DrawablePath, drawablePath, True)
  264. if ret:
  265. return ret
  266. else:
  267. os.rename(v4DrawablePath, drawablePath)
  268. return 0
  269. def removeSameRes(game, sdk, subChannel, config):
  270. '''
  271. 移除相同的资源
  272. '''
  273. # 读取sdk的资源
  274. print('remove same res...')
  275. sdkPath = file_utils.getFullSDKPath(sdk)
  276. sdkResPath = os.path.join(sdkPath, 'res')
  277. resList = []
  278. for path in os.listdir(sdkResPath):
  279. if not path.startswith('values'):
  280. continue
  281. absPath = os.path.join(sdkResPath, path)
  282. for resFile in os.listdir(absPath):
  283. '''if not resFile.startswith('jm_'):
  284. continue'''
  285. if resFile.endswith('.DS_Store'):
  286. continue
  287. #print('readAllRes -- > ' + os.path.join(absPath, resFile))
  288. resList = xml_utils.readAllRes(os.path.join(absPath, resFile), resList)
  289. if len(resList) == 0:
  290. print('no same res found')
  291. return 0
  292. # 移除相同的资源
  293. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  294. resPath = os.path.join(decompliePath, 'res')
  295. for path in os.listdir(resPath):
  296. if not path.startswith('values'):
  297. continue
  298. absPath = os.path.join(resPath, path)
  299. for resFile in os.listdir(absPath):
  300. xml_utils.removeSameRes(os.path.join(absPath, resFile), resList)
  301. return 0
  302. def mergeManifestRes(game, sdk, subChannel, config):
  303. '''
  304. 合并主文件
  305. '''
  306. print('merge AndroidManifest...')
  307. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  308. manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
  309. sdkPath = file_utils.getFullSDKPath(sdk)
  310. libManifest = file_utils.getFullPath(sdkPath, 'manifest.xml')
  311. return xml_utils.mergeManifestRes(manifest, libManifest)
  312. def copyAppRes(game, sdk, subChannel, config):
  313. '''
  314. 拷贝app的资源,比如app icon、启动图等
  315. '''
  316. channelPath = file_utils.getSubChannelPath(game, sdk, subChannel)
  317. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  318. # assets
  319. print('copy assets...')
  320. #适配一剑斩仙
  321. if game in adaptApp:
  322. print('适配一剑斩仙...')
  323. decomplieAssetsPath = file_utils.getFullPath(decompliePath, 'assets')
  324. skinZipPath = os.path.join(decomplieAssetsPath, 'skin.zip')
  325. skinPath = os.path.join(decomplieAssetsPath, 'skin')
  326. if os.path.exists(skinZipPath):
  327. with zipfile.ZipFile(skinZipPath) as zf:
  328. zf.extractall(decomplieAssetsPath)
  329. print('create unzip skin' )
  330. assetsPath = file_utils.getFullPath(channelPath, 'assets')
  331. decomplieAssetsPath = file_utils.getFullPath(decompliePath, 'assets')
  332. if os.path.exists(assetsPath):
  333. ret = file_utils.copyFileAllDir(assetsPath, decomplieAssetsPath)
  334. with zipfile.ZipFile(skinZipPath, 'w') as z:
  335. for root, dirs, files in os.walk(skinPath):
  336. for single_file in files:
  337. filepath = os.path.join(root, single_file)
  338. print ('create zip ---> ' + filepath)
  339. temPath = 'skin/' + single_file
  340. z.write(filepath, temPath)
  341. z.close()
  342. else:
  343. print('normal copy assets...')
  344. assetsPath = file_utils.getFullPath(channelPath, 'assets')
  345. decomplieAssetsPath = file_utils.getFullPath(decompliePath, 'assets')
  346. if os.path.exists(assetsPath):
  347. ret = file_utils.copyFileAllDir(assetsPath, decomplieAssetsPath)
  348. if ret:
  349. return ret
  350. else:
  351. print('normal copy assets...')
  352. assetsPath = file_utils.getFullPath(channelPath, 'assets')
  353. decomplieAssetsPath = file_utils.getFullPath(decompliePath, 'assets')
  354. if os.path.exists(assetsPath):
  355. ret = file_utils.copyFileAllDir(assetsPath, decomplieAssetsPath)
  356. if ret:
  357. return ret
  358. # icon
  359. print('copy icon...')
  360. ret = copyAppResWithType(decompliePath, channelPath, 'icon')
  361. if ret:
  362. return ret
  363. # 启动图
  364. print('copy splash...')
  365. ret = copyAppResWithType(decompliePath, channelPath, 'splash')
  366. if ret:
  367. return ret
  368. # 其他图片
  369. print('copy image...')
  370. ret = copyAppResWithType(decompliePath, channelPath, 'image')
  371. if ret:
  372. return ret
  373. return 0
  374. def copyAppResWithType(decompliePath, channelPath, typeName):
  375. decomplieResPath = os.path.join(decompliePath, 'res')
  376. iconPath = os.path.join(channelPath, typeName)
  377. if not os.path.exists(iconPath):
  378. print('dir "%s" not exists' % iconPath)
  379. return 0
  380. for d in os.listdir(iconPath):
  381. ret = copyResWithType(iconPath, decomplieResPath, d)
  382. if ret:
  383. return ret
  384. return 0
  385. def copyResWithType(resPath, decomplieResPath, typeName):
  386. # appt的打包目录会带-v4后缀
  387. resDir = os.path.join(resPath, typeName)
  388. target = os.path.join(decomplieResPath, typeName)
  389. targetV4 = os.path.join(decomplieResPath, typeName + '-v4')
  390. if not os.path.exists(target) and not os.path.exists(targetV4):
  391. os.makedirs(target)
  392. return file_utils.copyFileAllDir(resDir, target, False)
  393. elif not os.path.exists(target):
  394. return file_utils.copyFileAllDir(resDir, targetV4, False)
  395. else:
  396. return file_utils.copyFileAllDir(resDir, target, False)
  397. def removeNoSupportAttr(game, sdk, subChannel, config):
  398. '''
  399. 删除一些不支持的属性
  400. '''
  401. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  402. manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
  403. xml_utils.removeRootAttr(manifest, 'compileSdkVersion')
  404. xml_utils.removeRootAttr(manifest, 'compileSdkVersionCodename')
  405. return 0
  406. def fixUnSupportConfig(game, sdk, subChannel, config):
  407. '''
  408. 删除一些不支持的配置
  409. '''
  410. # 检查minSdkVersion
  411. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  412. yml = os.path.join(decompliePath, 'apktool.yml')
  413. minSdkVersion = 15
  414. file_utils.changeMinSdkVersion(yml, minSdkVersion)
  415. resPath = os.path.join(decompliePath, 'res')
  416. tag = '-v'
  417. for res in os.listdir(resPath):
  418. #print('res = ' + res)
  419. if res.startswith('values') and tag in res:
  420. start = res.index(tag)
  421. version = res[start+len(tag):]
  422. if not version.isdigit():
  423. continue
  424. version = int(version)
  425. print('version = %d' % version)
  426. if version < minSdkVersion:
  427. unSopportPath = os.path.join(resPath, res)
  428. print('unSopportPath = ' + unSopportPath)
  429. file_utils.deleteFolder(unSopportPath)
  430. print('deleteFolder = ' + unSopportPath)
  431. return 0
  432. def changePackageName(game, sdk, subChannel, config):
  433. '''
  434. 更改包名
  435. '''
  436. # 全局替换AndroidManifest里面的包名
  437. newPackageName = config['packageName']
  438. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  439. manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
  440. packageName = xml_utils.getPackageName(manifest)
  441. xml_utils.changePackageName(manifest, newPackageName)
  442. print('change package name %s --> %s' % (packageName, newPackageName))
  443. return 0
  444. def changeAppName(game, sdk, subChannel, config):
  445. '''
  446. 更改app名
  447. '''
  448. # 生成string.xml文件
  449. name = config['name']
  450. resName = 'common_sdk_name'
  451. if 'outName' in config:
  452. resName = resName + '_' + config['outName']
  453. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  454. stringFile = os.path.join(decompliePath, 'res', 'values', 'sdk_strings_temp.xml')
  455. content = '<?xml version="1.0" encoding="utf-8"?><resources><string name="%s">%s</string></resources>' % (resName, name)
  456. file_utils.createFile(stringFile, content)
  457. # 修改主文件的app名的值
  458. manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
  459. xml_utils.changeAppName(manifest, '@string/%s' % resName)
  460. print('change app name %s' % name)
  461. return 0
  462. def changeAppIcon(game, sdk, subChannel, config):
  463. '''
  464. 更改app icon
  465. '''
  466. print('change app icon...')
  467. resName = 'common_sdk_icon'
  468. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  469. manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
  470. xml_utils.changeAppIcon(manifest, '@mipmap/%s' % resName)
  471. return 0
  472. def addMetaData(game, sdk, subChannel, config):
  473. '''
  474. 添加meta-data
  475. '''
  476. if 'metaData' not in config:
  477. return 0
  478. print('add meta-data...')
  479. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  480. manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
  481. xml_utils.addMetaData(manifest, config['metaData'])
  482. return 0
  483. def addConfig(game, sdk, subChannel, config):
  484. '''
  485. 添加config.json
  486. '''
  487. if 'configData' not in config:
  488. return 0
  489. print('add config.json...')
  490. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  491. configJson = os.path.join(decompliePath, 'assets', 'jmhy_config.json')
  492. jsonText = json.dumps(config['configData'], ensure_ascii=False)
  493. file_utils.createFile(configJson, jsonText)
  494. return 0
  495. def changePlaceholders(game, sdk, subChannel, config):
  496. '''
  497. 处理掉占位符
  498. '''
  499. if 'placeholders' not in config:
  500. return 0
  501. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  502. manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
  503. placeholders = config['placeholders']
  504. for placeholder in placeholders:
  505. oldText = '${%s}' % placeholder
  506. newText = placeholders[placeholder]
  507. print('change placeholder %s -> %s' % (oldText, newText))
  508. file_utils.replaceContent(manifest, oldText, newText)
  509. return 0
  510. def addLauncher(game, sdk, subChannel, config):
  511. '''
  512. 添加启动图
  513. '''
  514. # ysdk的特殊处理
  515. if sdk in ignoreLauncher:
  516. return 0
  517. channelPath = file_utils.getSubChannelPath(game, sdk, subChannel)
  518. splashPath = os.path.join(channelPath, 'splash')
  519. if len(os.listdir(splashPath)) == 0:
  520. print('dir splash is empty')
  521. return 0
  522. print('add launcher...')
  523. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  524. manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
  525. activity = xml_utils.getLauncherActivityName(manifest)
  526. if activity == 'com.jmhy.sdk.template.LauncherActivity':
  527. print('add launcher already exist...')
  528. return 1
  529. # 添加关联资源
  530. internalPath = file_utils.getFullInternalPath()
  531. ret = copyAppResWithType(decompliePath, internalPath, 'launcher_res')
  532. if ret:
  533. return ret
  534. # 拷贝代码
  535. print('copy launcher code...')
  536. codePath = os.path.join(internalPath, 'launcher_code', 'smali')
  537. smaliPath = file_utils.getFullPath(decompliePath, 'smali')
  538. ret = file_utils.copyFileAllDir(codePath, smaliPath)
  539. if ret:
  540. return ret
  541. # 修改主文件信息
  542. print('change launcher config...')
  543. orientation = xml_utils.getScreenOrientation(manifest)
  544. activity = xml_utils.removeLauncherActivity(manifest)
  545. xml_utils.addLauncherActivity(manifest, orientation, 'com.jmhy.sdk.template.LauncherActivity')
  546. # 修改跳转的
  547. launcherActivity = os.path.join(decompliePath, 'smali', 'com', 'jmhy', 'sdk', 'template', 'LauncherActivity.smali')
  548. file_utils.replaceContent(launcherActivity, '{class}', activity)
  549. print('change launcher %s to %s' % (activity, 'com.jmhy.sdk.template.LauncherActivity'))
  550. # config['oldLauncher'] = activity
  551. if 'launcherTime' in config:
  552. timeHex = formatHex(config['launcherTime'])
  553. file_utils.replaceContent(launcherActivity, '0x0BB8', timeHex)
  554. return 0
  555. def addMoreIcon(game, sdk, subChannel, config):
  556. '''
  557. 添加多个图标
  558. '''
  559. print('add more icon support...')
  560. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  561. icon = '@mipmap/common_sdk_icon'
  562. if not config['changeIcon']:
  563. manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
  564. icon = xml_utils.getApplicationAttr(manifest, 'icon')
  565. switchIcon = icon
  566. if config['switchIcon']:
  567. switchIcon = '@mipmap/common_sdk_icon2'
  568. manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
  569. return xml_utils.addMoreIcon(manifest, icon, switchIcon)
  570. def copyIcon(game, sdk, subChannel, config):
  571. '''
  572. 复制icon到res ,一键登录使用
  573. '''
  574. if config['changeIcon']:
  575. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  576. decomplieResPath = file_utils.getFullPath(decompliePath, 'res')
  577. iconPath = os.path.join(decomplieResPath, 'mipmap-xhdpi', 'common_sdk_icon.png')
  578. desPath = os.path.join(decomplieResPath, 'drawable-hdpi', 'jm_cmcc_icon.png')
  579. file_utils.copyFile(iconPath, desPath)
  580. else:
  581. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  582. decomplieResPath = file_utils.getFullPath(decompliePath, 'res')
  583. desPath = os.path.join(decomplieResPath, 'drawable-hdpi', 'jm_cmcc_icon.png')
  584. manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
  585. icon = xml_utils.getApplicationAttr(manifest, 'icon')
  586. tag = icon[1:].split("/")
  587. if len(tag) < 1:
  588. return
  589. hdpi = ['-xhdpi', '-xxhdpi', '-xxxhdpi']
  590. for h in hdpi:
  591. p1 = '%s%s' % (tag[0],h)
  592. png = tag[1] + ".png"
  593. iconPath = os.path.join(decomplieResPath, p1, png)
  594. if os.path.exists(iconPath):
  595. print ('iconPath = ' + iconPath)
  596. file_utils.copyFile(iconPath, desPath)
  597. break
  598. def formatHex(millisecond):
  599. '''
  600. 将毫秒转为16进制,4位格式
  601. '''
  602. timeHex = str(hex(millisecond)).upper()
  603. timeHex = timeHex[2:]
  604. formatHex = ''
  605. if len(timeHex) == 3:
  606. formatHex = '0x0' + timeHexaddLauncher
  607. elif len(timeHex) == 4:
  608. formatHex = '0x' + timeHex
  609. else:
  610. formatHex = '0x0BB8'
  611. return formatHex
  612. def doSDKPostScript(game, sdk, config):
  613. '''
  614. 执行sdk相关特殊处理脚本
  615. '''
  616. sdkPath = file_utils.getFullSDKPath(sdk)
  617. scriptPath = os.path.join(sdkPath, 'script')
  618. targetScript = os.path.join(scriptPath, 'sdk_script.py')
  619. if not os.path.exists(targetScript):
  620. print('sdk_script no exists')
  621. return 0
  622. print('doSDKPostScript...')
  623. sys.path.append(scriptPath)
  624. module = importlib.import_module('sdk_script')
  625. ret = module.execute(game, sdk, config)
  626. sys.path.remove(scriptPath)
  627. return ret
  628. def doGamePostScript(game, sdk, config):
  629. '''
  630. 执行游戏相关特殊处理脚本
  631. '''
  632. channelPath = file_utils.getFullGamePath(game)
  633. scriptPath = os.path.join(channelPath, 'script')
  634. targetScript = os.path.join(scriptPath, 'game_script.py')
  635. if not os.path.exists(targetScript):
  636. print('game_script no exists')
  637. return 0
  638. print('doGamePostScript...')
  639. sys.path.append(scriptPath)
  640. module = importlib.import_module('game_script')
  641. ret = module.execute(game, sdk, config)
  642. sys.path.remove(scriptPath)
  643. return ret
  644. def doGameScript(game, sdk, config):
  645. '''
  646. 执行游戏相关特殊处理脚本
  647. '''
  648. channelPath = file_utils.getFullPath('game_script', game)
  649. targetScript = os.path.join(channelPath, 'game_script.py')
  650. print(targetScript + "--------")
  651. if not os.path.exists(targetScript):
  652. print('game_script no exists')
  653. return 0
  654. print('doGameScript...')
  655. sys.path.append(channelPath)
  656. module = importlib.import_module('game_script')
  657. ret = module.execute(game, sdk, config)
  658. sys.path.remove(channelPath)
  659. return ret
  660. def addLogSdk(game, sdk, subChannel, config, logSdk):
  661. # 拷贝jniLibs
  662. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  663. sdkPath = file_utils.getFullLogSDKPath(logSdk)
  664. print('copy log jniLibs...')
  665. jniPath = file_utils.getFullPath(sdkPath, 'jniLibs')
  666. decomplieJniPath = file_utils.getFullPath(decompliePath, 'lib')
  667. abiFilters = []
  668. if os.path.exists(decomplieJniPath):
  669. for abi in os.listdir(decomplieJniPath):
  670. if abi == 'armeabi-v7a' or abi == 'armeabi':
  671. abiFilters.append(abi)
  672. else:
  673. abiFilters = ['armeabi-v7a']
  674. if os.path.exists(jniPath):
  675. ret = file_utils.copyFileAllDir(jniPath, decomplieJniPath, False, abiFilters)
  676. if ret:
  677. return ret
  678. print('merge log AndroidManifest...')
  679. libManifest = file_utils.getFullPath(sdkPath, 'manifest.xml')
  680. if os.path.exists(libManifest):
  681. manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
  682. ret = xml_utils.mergeManifestRes(manifest, libManifest)
  683. if ret:
  684. return ret
  685. return packLogJar(game, sdk, subChannel, config, logSdk)
  686. def addOaidSdk(game, sdk, subChannel, config, oaidVersion):
  687. sdkPath = file_utils.getFullOaidSDKPath(oaidVersion)
  688. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  689. # 拷贝assets
  690. print('copy oaid assets...')
  691. assetsPath = file_utils.getFullPath(sdkPath, 'assets')
  692. decomplieAssetsPath = file_utils.getFullPath(decompliePath, 'assets')
  693. if os.path.exists(assetsPath):
  694. ret = file_utils.copyFileAllDir(assetsPath, decomplieAssetsPath)
  695. if ret:
  696. return ret
  697. # 拷贝jniLibs
  698. print('copy oaid jniLibs...')
  699. jniPath = file_utils.getFullPath(sdkPath, 'jniLibs')
  700. decomplieJniPath = file_utils.getFullPath(decompliePath, 'lib')
  701. abiFilters = []
  702. if os.path.exists(decomplieJniPath):
  703. for abi in os.listdir(decomplieJniPath):
  704. if abi == 'armeabi-v7a' or abi == 'armeabi':
  705. abiFilters.append(abi)
  706. else:
  707. abiFilters = ['armeabi-v7a']
  708. if os.path.exists(jniPath):
  709. ret = file_utils.copyFileAllDir(jniPath, decomplieJniPath, False, abiFilters)
  710. if ret:
  711. return ret
  712. print('merge oaid AndroidManifest...')
  713. libManifest = file_utils.getFullPath(sdkPath, 'manifest.xml')
  714. if os.path.exists(libManifest):
  715. manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
  716. ret = xml_utils.mergeManifestRes(manifest, libManifest)
  717. if ret:
  718. return ret
  719. return packOaidJar(game, sdk, subChannel, config, oaidVersion)
  720. def generateNewRFile(game, sdk, subChannel, config):
  721. '''
  722. 生成新的R文件
  723. '''
  724. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  725. androidPlatforms = file_utils.getAndroidCompileToolPath()
  726. manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
  727. decomplieResPath = file_utils.getFullPath(decompliePath, 'res')
  728. compliePath = file_utils.getFullPath(decompliePath, 'gen')
  729. if not os.path.exists(compliePath):
  730. os.makedirs(compliePath)
  731. # 生成R文件
  732. print('生成R文件 ...')
  733. if config['aapt2disable']:
  734. aapt = file_utils.getAAPTPath()
  735. ret = file_utils.getExecPermission(aapt)
  736. if ret:
  737. return ret
  738. createRCmd = '"%s" p -f -m -J "%s" -S "%s" -I "%s" -M "%s"' % (aapt, compliePath, decomplieResPath, androidPlatforms, manifest)
  739. ret = file_utils.execFormatCmd(createRCmd)
  740. if ret:
  741. return ret
  742. else:
  743. # compile
  744. aapt = file_utils.getAAPT2Path()
  745. ret = file_utils.getExecPermission(aapt)
  746. if ret:
  747. return ret
  748. print('compiled res ...')
  749. complieResPath = os.path.join(compliePath, 'compiled')
  750. complieResCmd = '"%s" compile --dir "%s" -o "%s"' % (aapt, decomplieResPath, complieResPath)
  751. file_utils.execFormatCmd(complieResCmd)
  752. # unzip
  753. print('unzip compiled res ...')
  754. unzipResPath = os.path.join(compliePath, 'aapt2_res')
  755. with zipfile.ZipFile(complieResPath) as zf:
  756. zf.extractall(unzipResPath)
  757. print('create unzip %s' % unzipResPath)
  758. # link
  759. print('link res ...')
  760. outApk = os.path.join(compliePath, 'res.apk')
  761. linkResCmd = '"%s" link -o "%s" -I "%s" --manifest "%s" --java "%s" --auto-add-overlay' % (aapt, outApk, androidPlatforms, manifest, compliePath)
  762. for filename in os.listdir(unzipResPath):
  763. linkResCmd += ' %s' % filename
  764. print('link cmd len is %s' % len(linkResCmd))
  765. ret = file_utils.execFormatCmd(linkResCmd, unzipResPath)
  766. if ret:
  767. return ret
  768. # 编译R文件
  769. print('complie R.java ...')
  770. packageName = xml_utils.getPackageName(manifest)
  771. packagePath = file_utils.getPackagePath(compliePath, packageName)
  772. RSourceFile = os.path.join(packagePath, 'R.java')
  773. complieRCmd = 'javac -source 1.8 -target 1.8 -encoding UTF-8 "%s"' % RSourceFile
  774. ret = file_utils.execFormatCmd(complieRCmd)
  775. if ret:
  776. return ret
  777. # 生成dex
  778. print('dex R.class ...')
  779. outDex = os.path.join(compliePath, 'classes.dex')
  780. if config['aapt2disable']:
  781. dx = file_utils.getDxPath()
  782. dexCmd = '--dex --no-warning --output="%s" "%s"' % (outDex, compliePath)
  783. else:
  784. dx = file_utils.getD8Path()
  785. clazz = os.path.join(packagePath, '*.class')
  786. dexCmd = '--lib "%s" --output "%s" %s' % (androidPlatforms, compliePath, clazz)
  787. ret = file_utils.execJarCmd(dx, dexCmd)
  788. if ret:
  789. return ret
  790. # 反向dex生成smali
  791. # 存放在out目录
  792. print('baksmali classes.dex ...')
  793. baksmaliPath = file_utils.getBaksmaliPath()
  794. outPath = file_utils.getFullPath(decompliePath, 'out')
  795. ret = file_utils.execJarCmd(baksmaliPath, 'd "%s" -o "%s"' % (outDex, outPath))
  796. if ret:
  797. return ret
  798. # 将生成的文件拷贝到目标目录
  799. print('copy R.smali ...')
  800. smaliPath = file_utils.getFullPath(decompliePath, 'smali')
  801. file_utils.copyFileAllDir(outPath, smaliPath)
  802. return 0
  803. def packJar(game, sdk, subChannel, config):
  804. '''
  805. 打包所有的jar
  806. '''
  807. splitDex = config['splitDex']
  808. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  809. outPath = file_utils.getFullPath(decompliePath, 'gen')
  810. if not os.path.exists(outPath):
  811. os.makedirs(outPath)
  812. if config['aapt2disable']:
  813. dx = file_utils.getDxPath()
  814. dexCmd = '--dex --no-warning --output="%s"' % outPath
  815. else:
  816. dx = file_utils.getD8Path()
  817. androidPlatforms = file_utils.getAndroidCompileToolPath()
  818. dexCmd = '--lib "%s" --output "%s"' % (androidPlatforms, outPath)
  819. # 找到所有lib依赖
  820. sdkPath = file_utils.getFullSDKPath(sdk)
  821. libs = os.path.join(sdkPath, 'libs')
  822. libConfig = os.path.join(libs, 'config.json')
  823. #
  824. # 存在配置文件
  825. if os.path.exists(libConfig):
  826. jsonText = file_utils.readFile(libConfig)
  827. libConf = json.loads(jsonText)
  828. if 'libConfig' in config and config['libConfig'] in libConf:
  829. conf = config['libConfig']
  830. libList = libConf[conf]
  831. for jar in libList:
  832. dexCmd += ' ' + os.path.join(libs, jar)
  833. elif 'default' in libConf:
  834. libList = libConf['default']
  835. for jar in libList:
  836. dexCmd += ' ' + os.path.join(libs, jar)
  837. else:
  838. for jar in os.listdir(libs):
  839. if not jar.endswith('.jar'):
  840. continue
  841. dexCmd += ' ' + os.path.join(libs, jar)
  842. else:
  843. for jar in os.listdir(libs):
  844. if not jar.endswith('.jar'):
  845. continue
  846. dexCmd += ' ' + os.path.join(libs, jar)
  847. # multidex.jar
  848. if splitDex:
  849. print('getMultiDexPath ...')
  850. dexCmd += ' ' + file_utils.getMultiDexPath()
  851. # sdk实现类
  852. print('packageing all jar ...')
  853. dexCmd += ' ' + os.path.join(sdkPath, '%s.jar' % sdk)
  854. ret = file_utils.execJarCmd(dx, dexCmd)
  855. if ret:
  856. return ret
  857. # 反向dex生成smali
  858. # 存放在out目录
  859. print('baksmali classes.dex ...')
  860. outDex = os.path.join(outPath, 'classes.dex')
  861. baksmaliPath = file_utils.getBaksmaliPath()
  862. outPath = file_utils.getFullPath(decompliePath, 'out')
  863. ret = file_utils.execJarCmd(baksmaliPath, 'd "%s" -o "%s"' % (outDex, outPath))
  864. if ret:
  865. return ret
  866. # 将生成的文件拷贝到目标目录
  867. print('copy all smali ...')
  868. smaliPath = file_utils.getFullPath(decompliePath, 'smali')
  869. ret = file_utils.copyFileAllDir(outPath, smaliPath, True)
  870. if ret:
  871. return ret
  872. return 0
  873. def packLogJar(game, sdk, subChannel, config, logSdk):
  874. '''
  875. 打包Log jar
  876. '''
  877. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  878. outPath = file_utils.getFullPath(decompliePath, 'gen')
  879. if not os.path.exists(outPath):
  880. os.makedirs(outPath)
  881. if config['aapt2disable']:
  882. dx = file_utils.getDxPath()
  883. dexCmd = '--dex --multi-dex --no-warning --output="%s"' % outPath
  884. else:
  885. dx = file_utils.getD8Path()
  886. androidPlatforms = file_utils.getAndroidCompileToolPath()
  887. dexCmd = '--lib "%s" --output "%s"' % (androidPlatforms, outPath)
  888. # 找到所有lib依赖
  889. sdkPath = file_utils.getFullLogSDKPath(logSdk)
  890. libs = os.path.join(sdkPath, 'libs')
  891. libConfig = os.path.join(libs, 'config.json')
  892. # 存在配置文件
  893. if os.path.exists(libConfig):
  894. jsonText = file_utils.readFile(libConfig)
  895. libList = json.loads(jsonText)
  896. for jar in libList:
  897. if not jar.endswith('.jar'):
  898. continue
  899. dexCmd += ' ' + os.path.join(libs, jar)
  900. else:
  901. for jar in os.listdir(libs):
  902. if not jar.endswith('.jar'):
  903. continue
  904. dexCmd += ' ' + os.path.join(libs, jar)
  905. # sdk实现类
  906. print('packageing all log jar ...')
  907. dexCmd += ' ' + os.path.join(sdkPath, '%s.jar' % logSdk)
  908. ret = file_utils.execJarCmd(dx, dexCmd)
  909. if ret:
  910. return ret
  911. # 反向dex生成smali
  912. # 存放在out目录
  913. print('baksmali classes.dex ...')
  914. outDex = os.path.join(outPath, 'classes.dex')
  915. baksmaliPath = file_utils.getBaksmaliPath()
  916. outPath = file_utils.getFullPath(decompliePath, 'out')
  917. ret = file_utils.execJarCmd(baksmaliPath, 'd "%s" -o "%s"' % (outDex, outPath))
  918. if ret:
  919. return ret
  920. # 将生成的文件拷贝到目标目录
  921. print('copy all log smali ...')
  922. smaliPath = file_utils.getFullPath(decompliePath, 'smali')
  923. ret = file_utils.copyFileAllDir(outPath, smaliPath, True)
  924. if ret:
  925. return ret
  926. return 0
  927. def packOaidJar(game, sdk, subChannel, config, oaidVerion):
  928. '''
  929. 打包oaid jar
  930. '''
  931. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  932. outPath = file_utils.getFullPath(decompliePath, 'gen')
  933. if not os.path.exists(outPath):
  934. os.makedirs(outPath)
  935. if config['aapt2disable']:
  936. dx = file_utils.getDxPath()
  937. dexCmd = '--dex --multi-dex --no-warning --output="%s"' % outPath
  938. else:
  939. dx = file_utils.getD8Path()
  940. androidPlatforms = file_utils.getAndroidCompileToolPath()
  941. dexCmd = '--lib "%s" --output "%s"' % (androidPlatforms, outPath)
  942. # 找到所有lib依赖
  943. sdkPath = file_utils.getFullOaidSDKPath(oaidVerion)
  944. libs = os.path.join(sdkPath, 'libs')
  945. libConfig = os.path.join(libs, 'config.json')
  946. # 存在配置文件
  947. # if os.path.exists(libConfig):
  948. # jsonText = file_utils.readFile(libConfig)
  949. # libList = json.loads(jsonText)
  950. # for jar in libList:
  951. # if not jar.endswith('.jar'):
  952. # continue
  953. # dexCmd += ' ' + os.path.join(libs, jar)
  954. # else:
  955. # for jar in os.listdir(libs):
  956. # if not jar.endswith('.jar'):
  957. # continue
  958. # dexCmd += ' ' + os.path.join(libs, jar)
  959. # sdk实现类
  960. print('packageing oaid jar ...')
  961. dexCmd += ' ' + os.path.join(libs, 'miit_mdid_%s.jar' % oaidVerion)
  962. ret = file_utils.execJarCmd(dx, dexCmd)
  963. if ret:
  964. return ret
  965. # 反向dex生成smali
  966. # 存放在out目录
  967. print('baksmali classes.dex ...')
  968. outDex = os.path.join(outPath, 'classes.dex')
  969. baksmaliPath = file_utils.getBaksmaliPath()
  970. outPath = file_utils.getFullPath(decompliePath, 'out')
  971. ret = file_utils.execJarCmd(baksmaliPath, 'd "%s" -o "%s"' % (outDex, outPath))
  972. if ret:
  973. return ret
  974. # 将生成的文件拷贝到目标目录
  975. print('copy all log smali ...')
  976. smaliPath = file_utils.getFullPath(decompliePath, 'smali')
  977. ret = file_utils.copyFileAllDir(outPath, smaliPath, True)
  978. if ret:
  979. return ret
  980. return 0
  981. def splitDex(game, sdk, subChannel, config):
  982. '''
  983. 分割dex
  984. '''
  985. # 判断是否已经存在application
  986. # 存在,则往原application添加内容
  987. # 不存在,则拷贝一个默认的android.support.multidex.MultiDexApplication
  988. print('add MultiDex support...')
  989. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  990. manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
  991. application = xml_utils.getApplicationAttr(manifest, 'name')
  992. if application is None:
  993. ret = xml_utils.changeApplicationAttr(manifest, 'name', 'android.support.multidex.MultiDexApplication')
  994. if ret:
  995. return ret
  996. else:
  997. smaliPath = os.path.join(decompliePath, 'smali')
  998. applicationFile = file_utils.getPackagePath(smaliPath, application)
  999. applicationFile += '.smali'
  1000. ret = changeApplicationDex(applicationFile)
  1001. if ret:
  1002. return ret
  1003. return splitSmali(game, sdk, subChannel, config, application)
  1004. def changeApplicationDex(file):
  1005. '''
  1006. 修改application的smali文件,增加MultiDex操作
  1007. '''
  1008. index = file_utils.getApplicationSmaliIndex(file)
  1009. file_utils.insertApplicationSmali(file, index)
  1010. return 0
  1011. def splitSmali(game, sdk, subChannel, config, application):
  1012. '''
  1013. 如果函数上限超过限制,自动拆分smali,以便生成多个dex文件
  1014. '''
  1015. print('splitSmali...')
  1016. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  1017. smaliPath = os.path.join(decompliePath, 'smali')
  1018. appPackage = None
  1019. if application:
  1020. appPackage = application[:application.rfind('.')]
  1021. appPackage = appPackage.replace('.', '/')
  1022. allFiles = []
  1023. allFiles = file_utils.list_files(smaliPath, allFiles)
  1024. #print('file count is %d' % len(allFiles))
  1025. #maxFuncNum = 65535
  1026. # 留一点空间,防止计算误差
  1027. maxFuncNum = 64000
  1028. currFucNum = 0
  1029. totalFucNum = 0
  1030. currDexIndex = 1
  1031. allRefs = []
  1032. #保证Application等类在第一个classex.dex文件中
  1033. for f in allFiles:
  1034. f = f.replace('\\', '/')
  1035. if (appPackage and appPackage in f) or '/android/support/multidex' in f:
  1036. currFucNum += smali_utils.get_smali_method_count(f, allRefs)
  1037. totalFucNum = currFucNum
  1038. for f in allFiles:
  1039. f = f.replace('\\', '/')
  1040. if not f.endswith('.smali'):
  1041. continue
  1042. if (appPackage and appPackage in f) or '/android/support/multidex' in f:
  1043. continue
  1044. thisFucNum = smali_utils.get_smali_method_count(f, allRefs)
  1045. totalFucNum += thisFucNum
  1046. #print('%d # %d ==> %s' % (thisFucNum, currDexIndex, f))
  1047. #print('totalFucNum is %d' % totalFucNum)
  1048. if currFucNum + thisFucNum >= maxFuncNum:
  1049. currFucNum = thisFucNum
  1050. currDexIndex += 1
  1051. newDexPath = os.path.join(decompliePath, 'smali_classes%d' % currDexIndex)
  1052. os.makedirs(newDexPath)
  1053. else:
  1054. currFucNum += thisFucNum
  1055. if currDexIndex > 1:
  1056. newDexPath = os.path.join(decompliePath, 'smali_classes%d' % currDexIndex)
  1057. targetFile = newDexPath + f[len(smaliPath):]
  1058. file_utils.copyFile(f, targetFile, True)
  1059. return 0
  1060. def changeVersion(game, sdk, subChannel, config):
  1061. '''
  1062. 更改版本号
  1063. '''
  1064. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  1065. yml = os.path.join(decompliePath, 'apktool.yml')
  1066. versionCode = None
  1067. versionName = None
  1068. targetSdkVersion = None
  1069. if 'versionCode' in config:
  1070. versionCode = config['versionCode']
  1071. if 'versionName' in config:
  1072. versionName = config['versionName']
  1073. if 'targetSdkVersion' in config:
  1074. targetSdkVersion = config['targetSdkVersion']
  1075. else:
  1076. targetSdkVersion = 26
  1077. print('changeVersion versionCode=%s,versionName=%s,targetSdkVersion=%s' % (versionCode, versionName, targetSdkVersion))
  1078. return file_utils.changeVersion(yml, versionCode, versionName, targetSdkVersion)
  1079. def recomplie(game, sdk, subChannel, config):
  1080. '''
  1081. 回编译
  1082. '''
  1083. print('recomplie apk...')
  1084. apktoolPath = file_utils.getApkToolPath()
  1085. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  1086. outApk = file_utils.getOutApkPath(game, sdk, subChannel, config['cache'])
  1087. useAppt2 = ' --use-aapt2'
  1088. if config['aapt2disable']:
  1089. useAppt2 = ''
  1090. return file_utils.execJarCmd(apktoolPath, 'b -f "%s" -o "%s"%s' % (decompliePath, outApk, useAppt2))
  1091. def alignApk(game, sdk, subChannel, config):
  1092. '''
  1093. 对齐apk
  1094. '''
  1095. print('align apk...')
  1096. outApk = file_utils.getOutApkPath(game, sdk, subChannel, config['cache'])
  1097. alignApk = file_utils.getAlignApkPath(game, sdk, subChannel, config['cache'])
  1098. alignapkTool = file_utils.getAlignPath()
  1099. if os.path.exists(alignApk):
  1100. os.remove(alignApk)
  1101. ret = file_utils.getExecPermission(alignapkTool)
  1102. if ret:
  1103. return ret
  1104. # zipalign.exe -v -p 4 input.apk output.apk
  1105. return file_utils.execFormatCmd('"%s" -f -p 4 "%s" "%s"' % (alignapkTool, outApk, alignApk))
  1106. def apksignerApk(game, sdk, subChannel, config):
  1107. '''
  1108. 签名apk
  1109. '''
  1110. print('sign apk...')
  1111. print('game = %s, sdk = %s, subChannel = %s, ...' % (game,sdk,subChannel))
  1112. path = os.path.join(file_utils.getCurrentPath(), 'keystore', 'key.json')
  1113. jsonText = file_utils.readFile(path)
  1114. signConfig = json.loads(jsonText)
  1115. keystore = {}
  1116. for key in signConfig.keys():
  1117. print(key)
  1118. if game.find(key) > -1 or game == key:
  1119. if sdk in signConfig[key]:
  1120. keystore = signConfig[key][sdk]
  1121. break
  1122. else:
  1123. keystore = signConfig['default']
  1124. else:
  1125. keystore = signConfig['default']
  1126. # if game in signConfig:
  1127. # if sdk in signConfig[game]:
  1128. # keystore = signConfig[game][sdk]
  1129. # else:
  1130. # keystore = signConfig['default']
  1131. # else:
  1132. # keystore = signConfig['default']
  1133. print('storeFile is "%s"' % keystore['storeFile'])
  1134. apksigner = file_utils.getApksignerPath()
  1135. alignApk = file_utils.getAlignApkPath(game, sdk, subChannel, config['cache'])
  1136. signedApk = file_utils.getSignApkPath(game, sdk, subChannel, config['cache'])
  1137. storeFile = os.path.join(file_utils.getCurrentPath(), 'keystore', keystore['storeFile'])
  1138. if 'outName' in config and 'outPath' in config:
  1139. if not os.path.exists(config['outPath']):
  1140. os.makedirs(config['outPath'])
  1141. signedApk = os.path.join(config['outPath'], config['outName'] + '.apk')
  1142. elif 'outName' in config:
  1143. signedApk = file_utils.getRenameApkPath(game, sdk, config['cache'], config['outName'])
  1144. # 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
  1145. v2disable = ''
  1146. if 'v2disable' in config and config['v2disable']:
  1147. v2disable = ' --v2-signing-enabled=false'
  1148. 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))
  1149. def addChannel(game, sdk, subChannel, config):
  1150. '''
  1151. 添加渠道信息
  1152. '''
  1153. if 'v2disable' in config and config['v2disable']:
  1154. return 0
  1155. walle = file_utils.getWallePath()
  1156. signedApk = file_utils.getSignApkPath(game, sdk, subChannel, config['cache'])
  1157. if 'outName' in config and 'outPath' in config:
  1158. signedApk = os.path.join(config['outPath'], config['outName'] + '.apk')
  1159. elif 'outName' in config:
  1160. signedApk = file_utils.getRenameApkPath(game, sdk, config['cache'], config['outName'])
  1161. properties = config['properties']
  1162. appid = ''
  1163. appkey = ''
  1164. host = ''
  1165. if 'appid' in properties:
  1166. appid = properties['appid']
  1167. if 'appkey' in properties:
  1168. appkey = properties['appkey']
  1169. if 'host' in properties:
  1170. host = properties['host']
  1171. return file_utils.execJarCmd(walle, 'put -e version=%s,agent=%s,appid=%s,appkey=%s,host=%s "%s" "%s"' % (config_utils.getDate(), properties['agent'], appid, appkey, host, signedApk, signedApk))
  1172. def clearTemp(game, sdk, subChannel, config):
  1173. '''
  1174. 清空中间产生的文件
  1175. '''
  1176. print('clear temp...')
  1177. alignApk = file_utils.getAlignApkPath(game, sdk, subChannel, config['cache'])
  1178. outApk = file_utils.getOutApkPath(game, sdk, subChannel, config['cache'])
  1179. if os.path.exists(alignApk):
  1180. os.remove(alignApk)
  1181. if os.path.exists(outApk):
  1182. os.remove(outApk)
  1183. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  1184. file_utils.deleteFolder(decompliePath)
  1185. print('clear temp end')
  1186. def packConsoleInput():
  1187. '''
  1188. 控制台打包
  1189. '''
  1190. if len(sys.argv) < 3:
  1191. print('argument is missing')
  1192. return 1
  1193. # 校验参数
  1194. game = sys.argv[1]
  1195. sdk = sys.argv[2]
  1196. # 可选参数,没有则默认打全部渠道
  1197. subChannel = None
  1198. if len(sys.argv) > 3:
  1199. subChannel = sys.argv[3]
  1200. return packConsole(game, sdk, subChannel)
  1201. def packConsole(game, sdk, subChannel):
  1202. '''
  1203. 控制台打包
  1204. '''
  1205. if not os.path.exists(file_utils.getFullGameApk(game)):
  1206. print('game "%s" not exists' % game)
  1207. return 1
  1208. if not os.path.exists(file_utils.getFullSDKPath(sdk)):
  1209. print('sdk "%s" not exists' % sdk)
  1210. return 1
  1211. global startTime
  1212. startTime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  1213. # 读取配置
  1214. channelPath = file_utils.getChannelPath(game, sdk)
  1215. configPath = os.path.join(channelPath, 'config.json')
  1216. if not os.path.exists(configPath):
  1217. print('%s not exists' % configPath)
  1218. return 1
  1219. jsonText = file_utils.readFile(configPath)
  1220. config = json.loads(jsonText)
  1221. # 检查参数
  1222. if not config_utils.checkConfig(config):
  1223. return 1
  1224. # 处理参数
  1225. config_utils.replaceArgs(config)
  1226. successCount = 0
  1227. failureCount = 0
  1228. if type(config) == dict:
  1229. if subChannel is None or config['subChannel'] == subChannel:
  1230. ret = pack(game, sdk, config)
  1231. if ret:
  1232. failureCount += 1
  1233. else:
  1234. successCount += 1
  1235. else:
  1236. print('subChannel "%s" no found' % subChannel)
  1237. return 1
  1238. elif type(config) == list:
  1239. found = False
  1240. for itemConfig in config:
  1241. if subChannel is None or itemConfig['subChannel'] == subChannel:
  1242. found = True
  1243. ret = pack(game, sdk, itemConfig)
  1244. if ret:
  1245. failureCount += 1
  1246. else:
  1247. successCount += 1
  1248. if not found:
  1249. print('subChannel "%s" no found' % subChannel)
  1250. return 1
  1251. print('success %d, failure %d' % (successCount, failureCount))
  1252. return 0
  1253. def formatXml(game, sdk, subChannel, config):
  1254. decompliePath = file_utils.getDecompliePath(game, sdk, subChannel, config['cache'])
  1255. manifest = os.path.join(decompliePath, 'AndroidManifest.xml')
  1256. return xml_utils.formatXml(manifest)