file_utils.py 14 KB

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