file_utils.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. from V2 import channel_special_replace
  2. from V2.print_log import printlog
  3. import os, xml.etree.ElementTree as ET, shutil, re
  4. def read_file(file):
  5. """
  6. 读取文件内容
  7. """
  8. content = ''
  9. with open_file(file, 'r') as f:
  10. content = f.read()
  11. return content
  12. # 文件修改时间比较
  13. # file1 > file2 return 1
  14. # file1 == file2 return 0
  15. # file1 < file2 return -1
  16. # 任意一个文件不存在则返回None
  17. def compareFileModifyTime(file1, file2):
  18. result = 0
  19. if not os.path.exists(file1):
  20. err = "file %s is not exists" % file1
  21. printlog(err)
  22. return None
  23. if not os.path.exists(file2):
  24. err = "file %s is not exists" % file2
  25. printlog(err)
  26. return None
  27. file1MTime = os.stat(file1).st_mtime
  28. file2MTime = os.stat(file2).st_mtime
  29. printlog("[file1MTime]: %s" % file1MTime)
  30. printlog("[file2MTime]: %s" % file2MTime)
  31. if file1MTime > file2MTime:
  32. return 1
  33. elif file1MTime < file2MTime:
  34. return -1
  35. else:
  36. return 0
  37. pass
  38. def open_file(file, mode):
  39. return open(file, mode, encoding='UTF-8')
  40. # 修改包名
  41. def modifyPkgName(apkPath, pkgName):
  42. manifestRoot = ET.parse("%s/AndroidManifest.xml" % apkPath).getroot()
  43. if manifestRoot is None:
  44. printlog("AndroidManifest.xml 文件解析失败")
  45. return 1
  46. if pkgName == "":
  47. printlog("包名未配置,保持母包包名")
  48. else:
  49. oldPkgName = manifestRoot.attrib["package"]
  50. content = open("%s/AndroidManifest.xml" % apkPath, "r").read()
  51. content = content.replace(oldPkgName, pkgName)
  52. open("%s/AndroidManifest.xml" % apkPath, "w").write(content)
  53. printlog("[old pkg name] : %s ----------------------------" % oldPkgName)
  54. printlog("[new pkg name] : %s ----------------------------" % pkgName)
  55. # sdkConfig.xml + AndroidManifest.xml
  56. # sdkConfigPath: 渠道配置路径
  57. # apkPath:解包后AndroidManifest.xml路径
  58. def mergeManifest(sdkConfigPath, apkManifestPath):
  59. printlog("------------------------- start to merge AndroidManifest.xml -------------------------")
  60. ret = 0
  61. # 注册命名空间
  62. ET.register_namespace('android', "http://schemas.android.com/apk/res/android")
  63. printlog("[apkManifestPath] : %s" % apkManifestPath)
  64. printlog("[sdkConfig] : %s" % sdkConfigPath)
  65. if os.path.exists(apkManifestPath) == False or os.path.exists(sdkConfigPath) == False:
  66. printlog("targetManifest or sdkConfigPath is not exists!")
  67. return 1
  68. sdkRoot = ET.parse(sdkConfigPath).getroot()
  69. if sdkRoot == None:
  70. printlog("cant parse sdkConfig!")
  71. return 1
  72. targetTree = ET.parse(apkManifestPath)
  73. targetRoot = targetTree.getroot()
  74. if targetRoot is None:
  75. printlog("cant parse targetTree")
  76. return 1
  77. targetApplicationRoot = targetRoot.find("application")
  78. # 合并activity和service
  79. sdkActivities = sdkRoot.find("sdk_activity").iter("activity")
  80. sdkActivities_alicas = sdkRoot.find("sdk_activity").iter("activity-alias")
  81. sdkServices = sdkRoot.find("sdk_activity").iter("service")
  82. sdkReceiver = sdkRoot.find("sdk_activity").iter("receiver")
  83. sdkProvider = sdkRoot.find("sdk_activity").iter("provider")
  84. for activity in sdkActivities:
  85. targetApplicationRoot.append(activity)
  86. for service in sdkServices:
  87. targetApplicationRoot.append(service)
  88. for receiver in sdkReceiver:
  89. targetApplicationRoot.append(receiver)
  90. for provider in sdkProvider:
  91. targetApplicationRoot.append(provider)
  92. for alicas in sdkActivities_alicas:
  93. targetApplicationRoot.append(alicas)
  94. printlog("merging activity and service completed!")
  95. # 合并meta-data
  96. sdkMetas = sdkRoot.find("sdk_meta").iter("meta-data")
  97. for meta in sdkMetas:
  98. targetApplicationRoot.append(meta)
  99. printlog("merging meta-data completed!")
  100. # 合并permission
  101. sdkPermissions = []
  102. for per in sdkRoot.find("sdk_permission").findall("uses-permission"):
  103. sdkPermissions.append(list(per.attrib.values())[0])
  104. targetPermissions = []
  105. for per in targetRoot.findall("uses-permission"):
  106. targetPermissions.append(list(per.attrib.values())[0])
  107. targetRoot.remove(per)
  108. # 利用set排重
  109. mergePermissions = list(set(sdkPermissions) | set(targetPermissions))
  110. for per in mergePermissions:
  111. element = ET.Element("uses-permission")
  112. element.attrib = {"android:name": per}
  113. element.tail = "\n\t"
  114. targetRoot.append(element)
  115. printlog("merging permission completed!")
  116. targetTree.write("%s" % apkManifestPath, "utf-8", xml_declaration=True)
  117. # 根据配置修改游戏名,包名等
  118. printlog("------------------------- finished merging AndroidManifest.xml -------------------------")
  119. return ret
  120. # 文件安全删除
  121. def safeFileDelete(filePath):
  122. if os.path.exists(filePath):
  123. if os.path.isdir(filePath):
  124. shutil.rmtree(filePath)
  125. printlog("[delete isdir filePath] : %s" % filePath)
  126. elif os.path.isfile(filePath):
  127. printlog("[delete isfile filePath] : %s" % filePath)
  128. os.remove(filePath)
  129. return
  130. # 删除文件但不删除文件夹
  131. def del_file(path_data):
  132. for i in os.listdir(path_data): # os.listdir(path_data)#返回一个列表,里面是当前目录下面的所有东西的相对路径
  133. file_data = path_data + "/" + i # 当前文件夹的下面的所有东西的绝对路径
  134. if os.path.isfile(file_data): # os.path.isfile判断是否为文件,如果是文件,就删除.如果是文件夹.递归给del_file.
  135. os.remove(file_data)
  136. else:
  137. del_file(file_data)
  138. # 复制并覆盖文件
  139. def copyAllFile(fromFile, toFile):
  140. for parent, dirName, fileNames in os.walk(fromFile):
  141. oriParent = parent.replace(fromFile, toFile)
  142. for fileName in fileNames:
  143. fromDir = "%s/%s" % (parent, fileName)
  144. # printlog("fromDir :%s"%fromDir)
  145. toDir = "%s/%s" % (oriParent, fileName)
  146. # printlog("toDir: %s"%toDir)
  147. copyFileForCompareFile(fromDir, toDir)
  148. pass
  149. def copy_file_all_dir(from_dir, to_dir, delete=False, support=None):
  150. """
  151. 拷贝目录下所有文件文件
  152. """
  153. ret = copy_dir(from_dir, to_dir, delete, support)
  154. if ret:
  155. return ret
  156. if delete:
  157. delete_folder(from_dir)
  158. return 0
  159. def copy_dir(from_dir, to_dir, delete=False, support=None):
  160. """
  161. 拷贝目录下所有文件文件
  162. """
  163. # print('copy all file %s --> %s' % (fromDir, toDir))
  164. if not os.path.exists(from_dir):
  165. print('%s not exists!' % from_dir)
  166. return 1
  167. if not os.path.isdir(from_dir):
  168. print('%s not a dir!' % from_dir)
  169. return 1
  170. for fromFile in os.listdir(from_dir):
  171. from_file_path = os.path.join(from_dir, fromFile)
  172. to_file_path = os.path.join(to_dir, fromFile)
  173. if os.path.isdir(from_file_path):
  174. # 不支持的类型
  175. if support is not None and fromFile not in support:
  176. continue
  177. ret = copy_dir(from_file_path, to_file_path, delete, support)
  178. if ret:
  179. return ret
  180. else:
  181. ret = copy_file(from_file_path, to_file_path, delete)
  182. if ret:
  183. return ret
  184. return 0
  185. def copy_file(from_file, to_file, delete=False):
  186. """
  187. 拷贝文件
  188. """
  189. if not os.path.isfile(from_file):
  190. print('----> %s not a file!' % from_file)
  191. return 1
  192. # 分离文件名和路径
  193. file_path, file_name = os.path.split(to_file)
  194. if not os.path.exists(file_path):
  195. # 创建路径
  196. os.makedirs(file_path)
  197. if delete:
  198. # 移动文件
  199. shutil.move(from_file, to_file)
  200. else:
  201. # 复制文件
  202. if not from_file.endswith('.DS_Store'):
  203. shutil.copyfile(from_file, to_file)
  204. return 0
  205. def copyFileForCompareFile(fromFile, toFile):
  206. if os.path.exists(fromFile) == False:
  207. printlog("file %s is not exists" % fromFile)
  208. return
  209. filePattern = re.compile(r".*\..*")
  210. toDir = toFile
  211. if filePattern.match(toFile):
  212. toDir = os.path.dirname(toFile)
  213. if not os.path.exists(toDir):
  214. os.makedirs(toDir)
  215. shutil.copy(fromFile, toFile)
  216. pass
  217. def change_min_sdk_version(yml, min_sdk_version):
  218. with open_file(yml, 'r+') as f:
  219. content = ''
  220. line = f.readline()
  221. while line:
  222. if 'minSdkVersion' in line:
  223. start = line.index("'")
  224. version = int(line[start + 1:-2])
  225. if version < min_sdk_version:
  226. content += ' minSdkVersion: \'%s\'\n' % min_sdk_version
  227. else:
  228. return 0
  229. else:
  230. content += line
  231. line = f.readline()
  232. f.seek(0, 0)
  233. f.truncate()
  234. f.write(content)
  235. return 0
  236. def open_file(file, mode):
  237. return open(file, mode, encoding='UTF-8')
  238. def delete_folder(folder):
  239. """
  240. 删除目录以及目录下的文件
  241. """
  242. if not os.path.exists(folder):
  243. return
  244. shutil.rmtree(folder)
  245. def iterate_dir_path(dir):
  246. path_list = []
  247. for path in os.listdir(dir):
  248. abs_path = os.path.join(dir, path)
  249. path_list.append(abs_path)
  250. return path_list
  251. def replace_content(file, old_txt, new_txt):
  252. """
  253. 全局替换
  254. """
  255. with open_file(file, 'r+') as f:
  256. t = f.read()
  257. t = t.replace(old_txt, new_txt)
  258. # 读写偏移位置移到最开始处
  259. f.seek(0, 0)
  260. # 清空内容
  261. f.truncate()
  262. f.write(t)
  263. def replace_manifest_meta_data(am_path, key, value):
  264. lines = open(am_path).readlines()
  265. fp = open(am_path, 'w')
  266. for s in lines:
  267. if s.find('android:name=\"' + key + '\"') != -1:
  268. printlog("Before replaceAM: %s" % s)
  269. s = re.sub('android:name=\"' + key + '\" android:value=\".*\"',
  270. 'android:name=\"' + key + '\" android:value=\"' + value + '\"', s)
  271. printlog("After replaceAM: %s" % s)
  272. fp.write(s)
  273. fp.close()
  274. pass
  275. def replace_assets_param(assets_param_path, key, value):
  276. lines = open(assets_param_path).readlines()
  277. fp = open(assets_param_path, 'w')
  278. for s in lines:
  279. s = re.sub(key + '=.*', key + '=' + value, s)
  280. fp.write(s)
  281. fp.close()
  282. pass
  283. def special_replace(sdk_name, prj_path, key, value):
  284. # 获取特殊替换
  285. printlog(" 开始进行渠道特殊处理 :%s" % sdk_name)
  286. replaceFun = channel_special_replace.get_special_replace(sdk_name)
  287. if replaceFun:
  288. replaceFun(prj_path, key, value)
  289. else:
  290. printlog(" 渠道无需特殊处理 :%s" % sdk_name)
  291. pass
  292. def replace_string_app_name(prj_path, value):
  293. if value is None:
  294. return
  295. # 命名空间,用于获取对应的值
  296. namespace = "http://schemas.android.com/apk/res/android"
  297. amTree = ET.parse("%s/AndroidManifest.xml" % prj_path)
  298. if amTree is None:
  299. return
  300. amRoot = amTree.getroot()
  301. applicationNode = amRoot.find("application")
  302. nameLabelAttrib = applicationNode.attrib.get("{%s}label" % namespace)[1:]
  303. nameLabelArr = nameLabelAttrib.split("/")
  304. nameLabelFile = nameLabelArr[0]
  305. nameLabel = nameLabelArr[1]
  306. modifyFileTree = ET.parse("%s/res/values/%ss.xml" % (prj_path, nameLabelFile))
  307. if modifyFileTree is None:
  308. return False
  309. modifyFileRoot = modifyFileTree.getroot()
  310. for child in list(modifyFileRoot):
  311. if child.attrib["name"] == nameLabel:
  312. child.text = value
  313. modifyFileTree.write("%s/res/values/%ss.xml" % (prj_path, nameLabelFile), "utf-8", xml_declaration=True)
  314. return True
  315. pass
  316. def replace_version_name(yml_path, value):
  317. if not os.path.exists(yml_path):
  318. return False
  319. replaceVersionNameShell = "/bin/sed -i -E \"s/(versionName:).*/\\1 %s/\" %s" % (value, yml_path)
  320. printlog("[replaceVersionNameShell] : %s" % replaceVersionNameShell)
  321. ret = os.system(replaceVersionNameShell)
  322. return ret
  323. pass
  324. def replace_version_code(yml_path, value):
  325. if not os.path.exists(yml_path):
  326. return False
  327. replaceVersionCodeShell = "/bin/sed -i -E \"s/(versionCode:).*/\\1 %s/\" %s" % (value, yml_path)
  328. printlog("[replaceVersionCodeShell] : %s" % replaceVersionCodeShell)
  329. ret = os.system(replaceVersionCodeShell)
  330. return ret
  331. pass
  332. def replace_yml_target_sdk_version(apkYmlPath, code):
  333. lines = open(apkYmlPath).readlines()
  334. fp = open(apkYmlPath, 'w')
  335. for line in lines:
  336. if 'targetSdkVersion' in line:
  337. newTargetVersion = " targetSdkVersion: '%s'" % code + '\n'
  338. printlog("oldTargetVersion:%s --------newTargetVersion:%s" % (line, newTargetVersion))
  339. line = re.sub(line, newTargetVersion, line)
  340. fp.write(line)
  341. fp.close()
  342. pass
  343. def changeVersion(yml, versionCode=None, versionName=None, targetSdkVersion=None):
  344. tag = ['versionCode:', 'versionName:', 'targetSdkVersion:']
  345. printlog('versionCode:%s versionName:%s targetSdkVersion:%s' % (versionCode, versionName, targetSdkVersion))
  346. with open_file(yml, 'r+') as f:
  347. content = ''
  348. line = f.readline()
  349. while line:
  350. if versionCode and tag[0] in line:
  351. content += ' versionCode: \'%s\'\n' % versionCode
  352. elif versionName and tag[1] in line:
  353. content += ' versionName: \'%s\'\n' % versionName
  354. elif targetSdkVersion and tag[2] in line:
  355. content += ' targetSdkVersion: \'%s\'\n' % targetSdkVersion
  356. else:
  357. content += line
  358. line = f.readline()
  359. f.seek(0, 0)
  360. f.truncate()
  361. f.write(content)
  362. def replace_file_keyword(file, keyword, value):
  363. if not os.path.exists(file):
  364. return False
  365. replaceVersionCodeShell = "/bin/sed -i -E \"s/(%s).*/\\1 %s/\" %s" % (keyword, value, file)
  366. printlog("[replaceVersionCodeShell] : %s" % replaceVersionCodeShell)
  367. ret = os.system(replaceVersionCodeShell)
  368. return ret
  369. pass
  370. # 修改微信回调页面
  371. def merge_wx_page(package_name, apk_decompile_tmp_dir):
  372. manifest_path = os.path.join(apk_decompile_tmp_dir, 'AndroidManifest.xml')
  373. manifest_root = ET.parse(manifest_path).getroot()
  374. namespace = '{http://schemas.android.com/apk/res/android}'
  375. for child in manifest_root.findall('application/activity'):
  376. if 'wxapi' in child.attrib[namespace + "name"]:
  377. old_package_name_path = child.attrib[namespace + "name"].split('.wxapi.')[0].replace(".", "/")
  378. old_wx_api_code_path = os.path.join(apk_decompile_tmp_dir, 'smali_classes2', old_package_name_path, 'wxapi')
  379. printlog('[old_wx_api_code_path]:%s' % old_wx_api_code_path)
  380. new_package_name_path = package_name.replace(".", "/")
  381. new_wx_api_code_path = os.path.join(apk_decompile_tmp_dir, 'smali_classes2', new_package_name_path, 'wxapi')
  382. printlog('[new_wx_api_code_path]:%s' % new_wx_api_code_path)
  383. if not os.path.exists(old_wx_api_code_path):
  384. printlog('微信回调代码不存在,不执行合并操作')
  385. return 0
  386. copy_file_all_dir(old_wx_api_code_path, new_wx_api_code_path, False)
  387. for parent, dirnames, filenames in os.walk(new_wx_api_code_path):
  388. for filename in filenames:
  389. abs_filename = os.path.join(new_wx_api_code_path, filename)
  390. printlog("[start to replace package_name]:%s" % abs_filename)
  391. replace_content(abs_filename, old_package_name_path, new_package_name_path)
  392. replace_content(manifest_path, old_package_name_path.replace('/', '.'), package_name)
  393. return
  394. if __name__ == "__main__":
  395. merge_wx_page('com.uxmfe.fgkg', '/Users/kaiweicai/Documents/Project/PackKit/YYXXPackKit/game/104082/dcm_tmphuawei')