package_utils_yfsdk.py 44 KB

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