package_utils_yfsdk.py 46 KB

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