file_utils.py 13 KB

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