file_utils.py 12 KB

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