file_utils.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. import os
  2. import os.path
  3. import shutil
  4. import subprocess
  5. import platform
  6. import sys
  7. import hashlib
  8. def getFullToolPath(name):
  9. '''
  10. 获取工具的目录
  11. '''
  12. return getFullPath('tools', name)
  13. def getFullGameApk(name):
  14. '''
  15. 获取游戏的原始包
  16. '''
  17. return getFullPath('game', name, name + '.apk')
  18. def getCacheGameApk(game, random, sdk):
  19. '''
  20. 获取游戏的原始包
  21. '''
  22. return getFullPath('game', game, sdk, random, game + '.apk')
  23. def getFullSDKPath(sdk):
  24. '''
  25. 获取sdk的目录
  26. '''
  27. return getFullPath('sdk', sdk)
  28. def getFullLogSDKPath(sdk, is_beta=False):
  29. '''
  30. 获取logsdk的目录
  31. '''
  32. if is_beta:
  33. return getFullPath('log_sdk_beta', sdk)
  34. else:
  35. return getFullPath('log_sdk', sdk)
  36. def getFullOaidSDKPath(version):
  37. '''
  38. 获取logsdk的目录
  39. '''
  40. return getFullPath('oaid_sdk', version)
  41. def getDecompliePath(game, sdk, subChannel, cache):
  42. '''
  43. 获取解包的目录
  44. '''
  45. return getFullPath('gen', game, sdk, subChannel, cache)
  46. def getSubChannelPath(game, sdk, subChannel):
  47. '''
  48. 获取子渠道的目录
  49. '''
  50. return getFullPath('game', game, sdk, subChannel)
  51. def getSubChannelPath(game, sdk, tag, subChannel):
  52. '''
  53. 获取子渠道的目录
  54. '''
  55. return getFullPath('game', game, sdk, tag, subChannel)
  56. def getChannelPath(game, sdk):
  57. '''
  58. 获取渠道的目录
  59. '''
  60. return getFullPath('game', game, sdk)
  61. def getChannelPath(game, tag, sdk):
  62. '''
  63. 获取渠道的目录
  64. '''
  65. return getFullPath('game', game, sdk, tag)
  66. def getFullGamePath(game):
  67. '''
  68. 获取游戏的目录
  69. '''
  70. return getFullPath('game', game)
  71. def getFullInternalPath():
  72. '''
  73. 获取内部目录
  74. '''
  75. return os.path.join(getCurrentPath(), 'internal')
  76. def getFullPath(type, *name):
  77. path = os.path.join(getCurrentPath(), type)
  78. for n in name:
  79. path = os.path.join(path, str(n))
  80. return path
  81. def getCurrentPath():
  82. '''
  83. 当前目录
  84. '''
  85. return sys.path[0]
  86. def execFormatCmd(cmd, cd=None):
  87. '''
  88. 执行cmd命令
  89. 返回值:None —— 子进程尚未结束;
  90. ==0 —— 子进程正常退出;
  91. > 0—— 子进程异常退出,returncode对应于出错码;
  92. < 0—— 子进程被信号杀掉了。
  93. '''
  94. '''print(cmd)
  95. p = os.popen(cmd)
  96. print(p.read())'''
  97. ret = 0
  98. try:
  99. s = subprocess.Popen(cmd, stdout=subprocess.PIPE,
  100. stderr=subprocess.PIPE, shell=True, cwd=cd)
  101. stdoutput, erroutput = s.communicate()
  102. if platform.system() == 'Windows':
  103. stdoutput = stdoutput.decode('gbk')
  104. erroutput = erroutput.decode('gbk')
  105. '''
  106. None —— 子进程尚未结束;
  107. ==0 —— 子进程正常退出;
  108. > 0—— 子进程异常退出,returncode对应于出错码;
  109. < 0—— 子进程被信号杀掉了。
  110. '''
  111. ret = s.returncode
  112. if ret:
  113. print('*******ERROR*******')
  114. print(stdoutput)
  115. print(erroutput)
  116. print('*******************')
  117. cmd = 'error::' + cmd + ' !!!exec Fail!!! '
  118. else:
  119. print(stdoutput)
  120. print(erroutput)
  121. cmd = cmd + ' !!!exec success!!! '
  122. print(cmd)
  123. except Exception as e:
  124. print('Exception ' + e)
  125. return 1
  126. return ret
  127. def execJarCmd(jar, params):
  128. '''
  129. 执行jar
  130. '''
  131. return execFormatCmd('java -jar "%s" %s' % (jar, params))
  132. def copyFileAllDir(fromDir, toDir, delete=False, support=None):
  133. '''
  134. 拷贝目录下所有文件文件
  135. '''
  136. # print('copy all file %s --> %s' % (fromDir, toDir))
  137. ret = copyDir(fromDir, toDir, delete, support)
  138. if ret:
  139. return ret
  140. if delete:
  141. deleteFolder(fromDir)
  142. return 0
  143. def copyDir(fromDir, toDir, delete=False, support=None):
  144. '''
  145. 拷贝目录下所有文件文件
  146. '''
  147. # print('copy all file %s --> %s' % (fromDir, toDir))
  148. if not os.path.exists(fromDir):
  149. print('%s not exists!' % fromDir)
  150. return 1
  151. if not os.path.isdir(fromDir):
  152. print('%s not a dir!' % fromDir)
  153. return 1
  154. for fromFile in os.listdir(fromDir):
  155. fromFilePath = os.path.join(fromDir, fromFile)
  156. toFilePath = os.path.join(toDir, fromFile)
  157. if os.path.isdir(fromFilePath):
  158. # 不支持的类型
  159. if support is not None and fromFile not in support:
  160. continue
  161. ret = copyDir(fromFilePath, toFilePath, delete, support)
  162. if ret:
  163. return ret
  164. else:
  165. ret = copyFile(fromFilePath, toFilePath, delete)
  166. if ret:
  167. return ret
  168. return 0
  169. def copyFile(formFile, toFile, delete=False):
  170. '''
  171. 拷贝文件
  172. '''
  173. if not os.path.isfile(formFile):
  174. print('----> %s not a file!' % formFile)
  175. return 1
  176. fpath, fname = os.path.split(toFile) # 分离文件名和路径
  177. if not os.path.exists(fpath):
  178. # print('%s not exists, crate' % fpath)
  179. os.makedirs(fpath) # 创建路径
  180. '''if os.path.exists(toFile) and os.path.getsize(formFile) > 104857600 and equalsFile(formFile, toFile):
  181. print('skip copy %s --> %s' % (formFile, toFile))
  182. if delete:
  183. os.remove(formFile)
  184. print('remove %s' % formFile)
  185. return 0'''
  186. if delete:
  187. shutil.move(formFile, toFile) # 移动文件
  188. # print('move %s --> %s' % (formFile, toFile))
  189. else:
  190. shutil.copyfile(formFile, toFile) # 复制文件
  191. # print('copy %s --> %s' % (formFile, toFile))
  192. return 0
  193. def replaceContent(file, oldText, newText):
  194. '''
  195. 全局替换
  196. '''
  197. with openFile(file, 'r+') as f:
  198. t = f.read()
  199. t = t.replace(oldText, newText)
  200. # 读写偏移位置移到最开始处
  201. f.seek(0, 0)
  202. # 清空内容
  203. f.truncate()
  204. f.write(t)
  205. def readFile(file):
  206. '''
  207. 读取文件内容
  208. '''
  209. content = ''
  210. with openFile(file, 'r') as f:
  211. content = f.read()
  212. return content
  213. def createFile(file, content):
  214. '''
  215. 创建文件
  216. '''
  217. fpath, fname = os.path.split(file) # 分离文件名和路径
  218. if not os.path.exists(fpath):
  219. os.makedirs(fpath)
  220. with openFile(file, 'w') as f:
  221. f.write(content)
  222. f.close()
  223. def openFile(file, mode):
  224. return open(file, mode, encoding='UTF-8')
  225. def deleteFolder(folder):
  226. '''
  227. 删除目录以及目录下的文件
  228. '''
  229. if not os.path.exists(folder):
  230. return
  231. # print('remove %s ...' % folder)
  232. shutil.rmtree(folder)
  233. # print('removed %s' % folder)
  234. def getAAPTPath():
  235. '''
  236. 获取aapt
  237. '''
  238. return getToolWithSystem('aapt')
  239. def getAAPT2Path():
  240. '''
  241. 获取aapt2
  242. '''
  243. return getToolWithSystem('aapt2')
  244. def getAndroidCompileToolPath():
  245. '''
  246. 获取android.jar
  247. '''
  248. return getFullToolPath('android.jar')
  249. def getDxPath():
  250. '''
  251. 获取dx.jar
  252. '''
  253. return getFullToolPath('dx.jar')
  254. def getD8Path():
  255. '''
  256. 获取d8.jar
  257. '''
  258. return getFullToolPath('d8.jar')
  259. def getAlignPath():
  260. '''
  261. 获取zipalign
  262. '''
  263. return getToolWithSystem('zipalign')
  264. def getMultiDexPath():
  265. '''
  266. 获取multidex.jar
  267. '''
  268. return getFullToolPath('android-support-multidex.jar')
  269. def getApkToolPath():
  270. '''
  271. 获取apktool.jar
  272. '''
  273. return getFullToolPath('apktool_2.4.0.jar')
  274. def getBaksmaliPath():
  275. '''
  276. 获取baksmali.jar
  277. '''
  278. return getFullToolPath('baksmali-2.3.jar')
  279. def getApksignerPath():
  280. '''
  281. 获取apksigner.jar
  282. '''
  283. return getFullToolPath('apksigner.jar')
  284. def getWallePath():
  285. '''
  286. 获取walle-cli-all.jar
  287. '''
  288. return getFullToolPath('walle-cli-all.jar')
  289. def getOutApkPath(game, sdk, subChannel, cache):
  290. '''
  291. 获取输出的apk的目录
  292. '''
  293. return getFullPath('target', game, sdk, cache, '%s_%s_%s.apk' % (game, sdk, subChannel))
  294. def getTargetApkPath(game, sdk, cache):
  295. '''
  296. 获取输出的apk的目录
  297. '''
  298. return getFullPath('target', game, sdk, cache)
  299. def getAlignApkPath(game, sdk, subChannel, cache):
  300. '''
  301. 获取输出的apk的目录
  302. '''
  303. return getFullPath('target', game, sdk, cache, '%s_%s_%s_align.apk' % (game, sdk, subChannel))
  304. def getSiginedApkPath(game, sdk, subChannel, cache):
  305. '''
  306. 获取输出的apk的目录
  307. '''
  308. return getFullPath('target', game, sdk, cache, '%s_%s_%s_signed.apk' % (game, sdk, subChannel))
  309. def getWalleApkPath(game, sdk, subChannel, cache):
  310. '''
  311. 获取输出的apk的目录
  312. '''
  313. return getFullPath('target', game, sdk, cache, '%s_%s_%s_walled.apk' % (game, sdk, subChannel))
  314. def getRenameApkPath(game, sdk, cache, name):
  315. '''
  316. 重命名输出的apk名称
  317. '''
  318. return getFullPath('target', game, sdk, cache, '%s.apk' % name)
  319. def getSignApkPath(game, sdk, subChannel, cache):
  320. '''
  321. 获取输出的apk的目录
  322. '''
  323. return getFullPath('target', game, sdk, cache, '%s_%s_%s_signed.apk' % (game, sdk, subChannel))
  324. def getPackagePath(basePath, packageName):
  325. '''
  326. 包名对应的目录
  327. '''
  328. packageNameSplit = packageName.split('.')
  329. newPath = basePath
  330. for item in packageNameSplit:
  331. newPath = os.path.join(newPath, item)
  332. return newPath
  333. def getToolWithSystem(tool):
  334. '''
  335. 获取系统相关工具
  336. '''
  337. system = getSystemPath()
  338. suffix = getSystemSuffix()
  339. return os.path.join(getFullToolPath(system), tool + suffix)
  340. def getSystemPath():
  341. '''
  342. 获取系统目录
  343. '''
  344. if platform.system() == 'Windows':
  345. return 'win'
  346. elif platform.system() == 'Darwin':
  347. print('---------macos----------')
  348. return 'macos'
  349. else:
  350. return 'linux'
  351. def getSystemSuffix():
  352. '''
  353. 系统工具后缀
  354. '''
  355. if platform.system() == 'Windows':
  356. return '.exe'
  357. else:
  358. return ''
  359. def getApplicationSmaliIndex(file):
  360. '''
  361. 获取application.smali的MultiDex信息
  362. '''
  363. content = ('invoke-super', '->attachBaseContext(Landroid/content/Context;)V')
  364. index = -1
  365. with openFile(file, 'r') as f:
  366. line = f.readline()
  367. while line:
  368. if content[0] in line and content[1] in line:
  369. index = f.tell()
  370. break
  371. line = f.readline()
  372. return index
  373. def insertApplicationSmali(file, index):
  374. '''
  375. 获取application.smali插入MultiDex的信息
  376. '''
  377. if index == -1:
  378. # append
  379. content = '''# virtual methods
  380. .method protected attachBaseContext(Landroid/content/Context;)V
  381. .locals 0
  382. .param p1, "context" # Landroid/content/Context;
  383. invoke-super {p0, p1}, Landroid/app/Application;->attachBaseContext(Landroid/content/Context;)V
  384. invoke-static {p0}, Landroid/support/multidex/MultiDex;->install(Landroid/content/Context;)V
  385. return-void
  386. .end method'''
  387. with openFile(file, 'a') as f:
  388. f.write(content)
  389. else:
  390. # insert
  391. content = '\n\tinvoke-static {p0}, Landroid/support/multidex/MultiDex;->install(Landroid/content/Context;)V\n'
  392. with openFile(file, 'r+') as f:
  393. f.seek(index)
  394. last = f.read()
  395. f.seek(index)
  396. f.write(content)
  397. f.write(last)
  398. return 0
  399. def changeVersion(yml, versionCode=None, versionName=None, targetSdkVersion=None):
  400. if versionCode is None and versionName is None and targetSdkVersion is None:
  401. return 0
  402. tag = ['versionCode:', 'versionName:', 'targetSdkVersion:']
  403. with openFile(yml, 'r+') as f:
  404. content = ''
  405. line = f.readline()
  406. while line:
  407. if versionCode is not None and tag[0] in line:
  408. content += ' versionCode: \'%s\'\n' % versionCode
  409. elif versionName is not None and tag[1] in line:
  410. content += ' versionName: %s\n' % versionName
  411. elif targetSdkVersion is not None and tag[2] in line:
  412. content += ' targetSdkVersion: \'%s\'\n' % targetSdkVersion
  413. else:
  414. content += line
  415. line = f.readline()
  416. f.seek(0, 0)
  417. f.truncate()
  418. f.write(content)
  419. return 0
  420. def changeMinSdkVersion(yml, minSdkVersion):
  421. with openFile(yml, 'r+') as f:
  422. content = ''
  423. line = f.readline()
  424. while line:
  425. if 'minSdkVersion' in line:
  426. start = line.index("'")
  427. version = int(line[start + 1:-2])
  428. if version < minSdkVersion:
  429. content += ' minSdkVersion: \'%s\'\n' % minSdkVersion
  430. else:
  431. return 0
  432. else:
  433. content += line
  434. line = f.readline()
  435. f.seek(0, 0)
  436. f.truncate()
  437. f.write(content)
  438. return 0
  439. def list_files(src, resFiles):
  440. '''
  441. 列出目录下所有的文件
  442. '''
  443. if os.path.exists(src):
  444. if os.path.isfile(src):
  445. resFiles.append(src)
  446. elif os.path.isdir(src):
  447. for f in os.listdir(src):
  448. list_files(os.path.join(src, f), resFiles)
  449. return resFiles
  450. def getFileMd5(f):
  451. '''
  452. 获取文件的md5
  453. '''
  454. m = hashlib.md5()
  455. while True:
  456. data = f.read(1024) # 将文件分块读取
  457. if not data:
  458. break
  459. m.update(data)
  460. return m.hexdigest()
  461. def equalsFile(file1, file2):
  462. '''
  463. 比较两个文件是否是同一个文件
  464. '''
  465. with open(file1, 'rb') as f1, open(file2, 'rb') as f2:
  466. file1Md5 = getFileMd5(f1)
  467. file2Md5 = getFileMd5(f2)
  468. # print('file1Md5:',file1Md5)
  469. # print('file2Md5:',file2Md5)
  470. return file1Md5 == file2Md5
  471. def getExecPermission(file):
  472. '''
  473. linux下获取执行权限
  474. '''
  475. if platform.system() == 'Windows':
  476. return 0
  477. return execFormatCmd('chmod +x "%s"' % file)
  478. def copySdkSmaliCode(game, sdk, subChannel, config):
  479. '''
  480. 拷贝代码
  481. '''
  482. print('copySdkSmaliCode --> %s' % sdk)
  483. sdkPath = getFullSDKPath(sdk)
  484. xmyFile = os.path.join(sdkPath, 'smali')
  485. decompliePath = getDecompliePath(game, sdk, subChannel, config['cache'])
  486. smaliPath = os.path.join(decompliePath, 'smali')
  487. ret = copyDir(xmyFile, smaliPath)
  488. return ret
  489. def copyGameSmaliCode(game, sdk, subChannel, config):
  490. '''
  491. 拷贝代码
  492. '''
  493. print('copyGameSmaliCode --> %s' % sdk)
  494. if not config['aapt2disable']:
  495. print('aapt2disable = false ~~~')
  496. return 0
  497. path = os.path.join(getCurrentPath(), "game_script", game, "gameSmali")
  498. print('copyDir = gameSmali~~~')
  499. decompliePath = getDecompliePath(game, sdk, subChannel, config['cache'])
  500. smaliPath = os.path.join(decompliePath, 'smali')
  501. ret = copyDir(path, smaliPath)
  502. return ret