PackTools.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. import platform
  4. import os
  5. import shutil
  6. import sys
  7. import getopt
  8. import json
  9. import subprocess
  10. import traceback
  11. import re
  12. from Common import Setting, Debug, PathConvert, StringFormat
  13. Setting.SetDefaultencodingUTF8()
  14. def StrAlign(s, n, c=' '):
  15. if n == 0:
  16. return s
  17. slen = len(s)
  18. re = s
  19. if isinstance(s, str):
  20. placeholder = ' '
  21. else:
  22. placeholder =u' '
  23. while slen < n:
  24. re += placeholder
  25. slen += 1
  26. return re
  27. UNITY_VERSION_STR = "Unity 2018.4.1f1"
  28. UNITY_VERSION = "Unity2018.4.1f1"
  29. CUR_PATH = os.path.abspath(sys.path[0])
  30. PROJECT_PATH = os.path.abspath(CUR_PATH + '/../../..')
  31. BUILD_LOG_PATH = os.path.abspath(CUR_PATH + '/../logs')
  32. BUILD_CONFIG_PATH = os.path.abspath(
  33. CUR_PATH + '/../../BuildDependenceResource/PackConfig.json')
  34. FAIR_GUARD_PATH = os.path.abspath(CUR_PATH + '/../FairGuard')
  35. FAIR_GUARD_ANDROID_PATH = os.path.abspath(FAIR_GUARD_PATH + '/Android')
  36. FAIR_GUARD_ANDROID_JAR_PATH = os.path.abspath(FAIR_GUARD_ANDROID_PATH + '/FairGuard.jar')
  37. FAIR_GUARD_ANDROID_CONFIG_PATH = os.path.abspath(
  38. FAIR_GUARD_ANDROID_PATH + '/config.ini')
  39. FAIR_GUARD_ANDROID_MODULES_PATH = os.path.abspath(FAIR_GUARD_ANDROID_PATH + '/modules')
  40. FAIR_GUARD_iOS_PATH = os.path.abspath(FAIR_GUARD_PATH + '/iOS')
  41. FAIR_GUARD_iOS_BUILD_PATH = os.path.abspath(FAIR_GUARD_iOS_PATH + '/fairguardbuild')
  42. CUR_PLATFORM_NAME = platform.system()
  43. if CUR_PLATFORM_NAME == 'Windows':
  44. from _winreg import *
  45. VALID_CMD_ARGV = {"-help", "-unitypath",
  46. "-config", "-buildAB", "-buildApp", "-nogit",
  47. "-clrerr"
  48. }
  49. PlatformMap = {
  50. 'Android' : ['androids', 'weiDuanAndroids'],
  51. 'iOS' : ['iOSs'],
  52. 'PC' : ['pCs']
  53. }
  54. ConfigPlatformMap = {}
  55. for key in PlatformMap:
  56. for tag in PlatformMap[key]:
  57. ConfigPlatformMap[tag] = key
  58. UnityLogLevelTagMap = {
  59. 'debug' : ['[PACK_RUN START]', '[PACK_RUN END]'],
  60. 'waring' : ['[PACK_RUN WARING START]', '[PACK_RUN WARING END]'],
  61. 'error' : ['[PACK_RUN ERROR START]', '[PACK_RUN ERROR END]'],
  62. 'exception' : ['[PACK_RUN EXCEPTION START]', '[PACK_RUN EXCEPTION END]']
  63. }
  64. UnityLogLevelMethod = {
  65. 'debug' : Debug.Log,
  66. 'waring' : Debug.LogWaring,
  67. 'error' : Debug.LogError,
  68. 'exception' : Debug.LogException
  69. }
  70. UnityLogStartMap = {}
  71. UnityLogEndMap = {}
  72. UnityTagLogLevelMap = {}
  73. for key in UnityLogLevelTagMap:
  74. tags = UnityLogLevelTagMap[key]
  75. UnityLogStartMap[tags[0]] = key
  76. UnityLogEndMap[tags[1]] = key
  77. UnityTagLogLevelMap[tags[0]] = key
  78. UnityTagLogLevelMap[tags[1]] = key
  79. UNITY_TAG_ResOutPath = "[ResOutPath]"
  80. UNITY_TAG_AppOutPath = "[AppOutPath]"
  81. UNITY_TAG_ICON_PATH = "[ICON_PATH]"
  82. TAG_KEYSTORE_PATH = "[KEYSTORE_PATH]"
  83. TAG_OTHER_BUILD_PROJECT_PATH = "[OTHER_BUILD_PROJECT_PATH]"
  84. UNITY_TAG_ANDROID_JAVA_ROOT = "[ANDROID_JAVA_ROOT]"
  85. UNITY_TAG_ANDROID_SDK_ROOT = "[ANDROID_SDK_ROOT]"
  86. UNITY_TAG_ANDROID_NDK_ROOT = "[ANDROID_NDK_ROOT]"
  87. def PrintHelp():
  88. Debug.Log('')
  89. Debug.Log('')
  90. Debug.Log('命令参数说明')
  91. Debug.Log('-help 查看帮助')
  92. Debug.Log('-unitypath Unity安装文件路径')
  93. Debug.Log('-config 查看可编译的平台')
  94. Debug.Log(' 参数为空,则显示可编译目标平台')
  95. Debug.Log(' 参数不为空,如参数为Android 则显示android渠道的编译目标')
  96. Debug.Log('-nogit 关闭执行Git, 默认会执行git')
  97. Debug.Log(' 主要执行命令 git checkout . & git pull')
  98. Debug.Log('')
  99. Debug.Log('')
  100. Debug.Log('编译命令说明')
  101. Debug.Log(' -buildApp 编译并生成App')
  102. Debug.Log(' 编译多个时请使用英文符号\":\"隔开')
  103. Debug.Log(' -clrerr 解决项目编译错误')
  104. Debug.Log('')
  105. Debug.Log('')
  106. def ValidJava():
  107. try:
  108. cmds = ["java", "-version"]
  109. sp = subprocess.Popen(cmds, stdout=subprocess.PIPE,
  110. stderr=subprocess.PIPE)
  111. contents = sp.communicate()
  112. sp.wait()
  113. version = None
  114. for content in contents:
  115. lines = content.split("\n")
  116. for i in range(len(lines)):
  117. line = lines[i].strip()
  118. if line.startswith("java version "):
  119. version = line.replace("java version ", "")
  120. version = version.replace("\"", "")
  121. if not version:
  122. Debug.LogError("无法检测JDK版本 ")
  123. return False
  124. else:
  125. if version.startswith("1.8"):
  126. return True
  127. else:
  128. Debug.LogError("JDK版本不符,需要使用1.8版本 当前版本 " + version)
  129. return False
  130. except Exception as exc:
  131. Debug.LogError(exc)
  132. Debug.Log("")
  133. Debug.LogError("无法找到有效JDK, 请检查环境 !!! ")
  134. return False
  135. return True
  136. def ValidUnityPath(path):
  137. if path:
  138. if os.path.exists(path):
  139. with open(path, 'rb') as file_obj:
  140. content = file_obj.read()
  141. if UNITY_VERSION_STR in content:
  142. return True
  143. Debug.LogError(
  144. path + " 不是" + UNITY_VERSION_STR + "版本,请使用-unitypath来设置路径")
  145. return False
  146. else:
  147. Debug.LogError(
  148. path + " Unity安装路径是不存在的,请使用-unitypath来设置路径")
  149. return False
  150. Debug.LogError("Unity安装路径是不存在的,请使用-unitypath来设置路径")
  151. return False
  152. def ExecGit():
  153. try:
  154. os.chdir(PROJECT_PATH)
  155. cmds = ["git", "checkout", "."]
  156. res = subprocess.check_output(cmds, shell=False)
  157. res = StringFormat.FormatStdinString(res)
  158. Debug.Log(res)
  159. cmds = ["git", "pull"]
  160. res = subprocess.check_output(cmds, shell=False)
  161. res = StringFormat.FormatStdinString(res)
  162. Debug.Log(res)
  163. except Exception as exc:
  164. Debug.LogError(exc)
  165. Debug.Log("")
  166. Debug.LogError("git 执行失败, 请检查环境 !!! ")
  167. return False
  168. return True
  169. def GetConfigs():
  170. with open(BUILD_CONFIG_PATH) as file_obj:
  171. content = '\n'.join(file_obj.readlines())
  172. packPlatforms = json.loads(content)
  173. return packPlatforms
  174. def GetPackInfo(packId):
  175. packPlatforms = GetConfigs()
  176. for key in ConfigPlatformMap:
  177. if not packPlatforms.has_key(key):
  178. continue
  179. datas = packPlatforms[key]
  180. for idx in range(len(datas)):
  181. data = datas[idx]
  182. if data["channelUniqueId"] == packId:
  183. return data, key, ConfigPlatformMap[key]
  184. return None, None
  185. def GetPackName(packInfo):
  186. return packInfo['channelName'] + "/" + packInfo['appName'] + "/" + packInfo['distributeName']
  187. def GetVersionCode(versionCode):
  188. return (versionCode['major'] * 1000000 + versionCode['minor'] * 10000 + versionCode['release'] * 100 + versionCode['patch'])
  189. def GetVersionName(versionCode):
  190. return str(versionCode['major']) + "." + str(versionCode['minor']) + "." + str(versionCode['release']) + "." + str(versionCode['patch'])
  191. def ChannelUniqueIdSort(elem):
  192. return int(elem['data']['channelUniqueId'])
  193. def PrintConfig(platform):
  194. packPlatforms = GetConfigs()
  195. if not platform or platform == '':
  196. Debug.Log("")
  197. Debug.Log("")
  198. Debug.Log("可编译所有平台")
  199. Debug.Log(" all")
  200. Debug.Log("")
  201. Debug.Log("可编译平台")
  202. for key in PlatformMap:
  203. Debug.Log(" " + key)
  204. Debug.Log("")
  205. Debug.LogWaring("上述命令都忽略大小写", True)
  206. Debug.Log("")
  207. Debug.Log("")
  208. else:
  209. platform = platform.lower()
  210. datas = []
  211. for key in PlatformMap:
  212. if platform == key.lower():
  213. ls = PlatformMap[key]
  214. for i in range(len(ls)):
  215. cfgDatas = packPlatforms[ls[i]]
  216. for j in range(len(cfgDatas)):
  217. datas.append({'data' : cfgDatas[j], 'name' : key})
  218. if not datas or len(datas) <= 0:
  219. if platform == 'all':
  220. datas = []
  221. for key in ConfigPlatformMap:
  222. cfgDatas = packPlatforms[key]
  223. for j in range(len(cfgDatas)):
  224. datas.append({'data' : cfgDatas[j], 'name' : ConfigPlatformMap[key]})
  225. # datas.extend(packPlatforms[key])
  226. else:
  227. PrintConfig(None)
  228. return
  229. Debug.Log("")
  230. Debug.Log("")
  231. lastId = None
  232. datas.sort(key=ChannelUniqueIdSort)
  233. for i in range(len(datas)):
  234. data = datas[i]['data']
  235. curId = int(data['channelUniqueId'])
  236. if lastId and abs(curId - lastId) > 1:
  237. Debug.Log('')
  238. lastId = curId
  239. log = '{}\t{}\t{}\t{}'.format(StrAlign(data['channelUniqueId'], 8), StrAlign("(" + GetVersionName(data['gameVersionCode']) + "),(" + GetVersionName(data['resVersionCode']) + ")", 30), StrAlign(datas[i]['name'], 8), GetPackName(data))
  240. Debug.Log(log)
  241. Debug.Log("")
  242. Debug.Log("")
  243. def GetUnityPath(path):
  244. if path:
  245. return path
  246. if CUR_PLATFORM_NAME == 'Windows':
  247. key = OpenKey(HKEY_CLASSES_ROOT, 'com.unity3d.kharma\DefaultIcon')
  248. if key:
  249. value, type = QueryValueEx(key, '')
  250. return value
  251. elif CUR_PLATFORM_NAME == 'Darwin':
  252. return "//Applications/" + UNITY_VERSION + "/"+ UNITY_VERSION + ".app/Contents/MacOS/Unity"
  253. return None
  254. def ParseUnityLog(logPath):
  255. if not os.path.exists(logPath):
  256. raise Exception(logPath + "不存在")
  257. logTagLines = {}
  258. params = {}
  259. with open(logPath) as file_obj:
  260. lines = file_obj.readlines()
  261. curLogTag = None
  262. logTags = []
  263. validLines = {}
  264. for i in range(len(lines)):
  265. line = lines[i].strip()
  266. if UnityLogEndMap.has_key(line):
  267. if not curLogTag or curLogTag != UnityLogEndMap[line]:
  268. raise Exception(logPath + '解析错误')
  269. if len(validLines) > 0:
  270. if logTagLines.has_key(curLogTag):
  271. loglines = logTagLines[curLogTag]
  272. else:
  273. loglines = {}
  274. logTagLines[curLogTag] = loglines
  275. loglines.update(validLines)
  276. curLogTag = None
  277. logTags.pop()
  278. if curLogTag is not None:
  279. validLines[i] = line
  280. if line.startswith('['):
  281. idx = line.find(']')
  282. if idx >= 0:
  283. key = line[0:idx+1]
  284. value = line[idx+1:len(line)]
  285. if key and value:
  286. params[key] = value
  287. if UnityLogStartMap.has_key(line):
  288. curLogTag = UnityLogStartMap[line]
  289. logTags.append(curLogTag)
  290. return logTagLines, params
  291. def PrintUnityLog(logPath, tags = ['debug', 'waring', 'error', 'exception']):
  292. try:
  293. logTagLines,_ = ParseUnityLog(logPath)
  294. lineIds = []
  295. if tags:
  296. for i in range(len(tags)):
  297. if logTagLines.has_key(tags[i]):
  298. keys = logTagLines[tags[i]].keys()
  299. lineIds.extend(keys)
  300. lineIds.sort()
  301. for i in range(len(lineIds)):
  302. for j in range(len(tags)):
  303. tag = tags[j]
  304. if logTagLines.has_key(tag) and logTagLines[tag].has_key(lineIds[i]) :
  305. UnityLogLevelMethod[tag](logTagLines[tag][lineIds[i]], True)
  306. break
  307. except Exception, exc:
  308. Debug.LogError(exc)
  309. def DeleteUnityLog(logPath):
  310. if os.path.exists(logPath):
  311. with open(logPath, 'w') as file_obj:
  312. file_obj.write("")
  313. def UnityPack(unityPath, packId, logPath):
  314. Debug.LogSuccess("Unity3d打包开始")
  315. if not os.path.exists(logPath):
  316. if not os.path.exists(os.path.dirname(logPath)):
  317. os.mkdir(os.path.dirname(logPath))
  318. cmds = [unityPath, "-quit", "-batchmode",
  319. "-projectPath", PROJECT_PATH, "-logFile", logPath, "-executeMethod", "Pack.PackRun.CommandLineBuildApp"]
  320. # if method == 1:
  321. # cmds.append("-buildAB")
  322. # cmds.append(packId)
  323. # elif method == 2:
  324. cmds.append("-buildApp")
  325. cmds.append(packId)
  326. # else:
  327. # cmds.append("-h")
  328. try:
  329. res = subprocess.check_output(cmds, shell=False)
  330. except Exception as exc:
  331. PrintUnityLog(logPath)
  332. Debug.LogError(exc)
  333. Debug.Log("")
  334. Debug.LogError("Unity3d打包失败")
  335. return False
  336. Debug.LogSuccess("Unity3d打包成功")
  337. return True
  338. def FairGuardiOS(appPath):
  339. if CUR_PLATFORM_NAME == 'Darwin':
  340. Debug.LogError("当前不在Mac OS系统环境中,无法加固")
  341. return False
  342. if not appPath or appPath == "" or not os.path.exists(StringFormat.FormatStringPlatform(appPath)):
  343. Debug.LogError(appPath + " 无法找到上次打包的App, FairGuard加固失败")
  344. return False
  345. if not os.path.exists(FAIR_GUARD_iOS_PATH):
  346. Debug.LogError(FAIR_GUARD_iOS_PATH + " 无法找到加固工具, FairGuard加固失败")
  347. return False
  348. os.chdir(FAIR_GUARD_iOS_PATH)
  349. Debug.LogSuccess("FairGuard加固开始 赋予fairguardbuild可执行权限")
  350. import stat
  351. file = FAIR_GUARD_iOS_BUILD_PATH
  352. os.chmod(file, os.stat(file).st_mode | stat.S_IEXEC)
  353. Debug.LogSuccess("FairGuard加固完成 赋予fairguardbuild可执行权限")
  354. if not os.path.exists(FAIR_GUARD_iOS_BUILD_PATH):
  355. Debug.LogError(FAIR_GUARD_iOS_BUILD_PATH + " 无法找到加固工具, FairGuard加固失败")
  356. return False
  357. Debug.LogSuccess("FairGuard加固开始 " + appPath)
  358. try:
  359. cmds = ["/base/sh", FAIR_GUARD_iOS_BUILD_PATH,
  360. "-p",
  361. StringFormat.FormatStringPlatform(appPath) + "/Unity-iPhone.xcodeproj",
  362. "-W"]
  363. res = subprocess.check_output(cmds, shell=False, stderr=STDOUT)
  364. res = StringFormat.FormatStdinString(res)
  365. success = False
  366. Debug.Log(res)
  367. # lines = res.split("\n")
  368. # for i in range(len(lines)):
  369. # line = lines[i].strip()
  370. # if line.endswith("game protect suceess!"):
  371. # success = True
  372. if not success:
  373. Debug.Log(res)
  374. Debug.LogError("")
  375. Debug.LogError("FairGuard加固失败 " + appPath)
  376. return False
  377. except Exception as exc:
  378. Debug.LogError(exc)
  379. Debug.Log("")
  380. Debug.LogError("FairGuard加固失败 " + appPath)
  381. return False
  382. Debug.LogSuccess("FairGuard加固完成 " + appPath)
  383. return True
  384. def FairGuardAndroid(appPath):
  385. if not appPath or appPath == "" or not os.path.exists(StringFormat.FormatStringPlatform(appPath)):
  386. Debug.LogError(appPath + " 无法找到上次打包的App, FairGuard加固失败")
  387. return False
  388. if not os.path.exists(FAIR_GUARD_ANDROID_MODULES_PATH):
  389. Debug.LogError(FAIR_GUARD_ANDROID_MODULES_PATH + " 无法找到加固工具, FairGuard加固失败")
  390. return False
  391. os.chdir(FAIR_GUARD_ANDROID_MODULES_PATH)
  392. if CUR_PLATFORM_NAME == 'Linux' or CUR_PLATFORM_NAME == 'Darwin':
  393. Debug.LogSuccess("FairGuard加固开始 赋予modules目录可执行权限")
  394. import stat
  395. for root, dirs, files in os.walk("."):
  396. for file in files:
  397. os.chmod(file, os.stat(file).st_mode | stat.S_IEXEC)
  398. Debug.LogSuccess("FairGuard加固完成 赋予modules目录可执行权限")
  399. if not os.path.exists(FAIR_GUARD_ANDROID_PATH):
  400. Debug.LogError(FAIR_GUARD_ANDROID_PATH + " 无法找到加固工具, FairGuard加固失败")
  401. return False
  402. os.chdir(FAIR_GUARD_ANDROID_PATH)
  403. if not os.path.exists(FAIR_GUARD_ANDROID_CONFIG_PATH):
  404. Debug.LogError(FAIR_GUARD_ANDROID_CONFIG_PATH + " 无法找到加固用的证书, FairGuard加固失败")
  405. return False
  406. certPath = None
  407. if not os.path.exists(FAIR_GUARD_ANDROID_JAR_PATH):
  408. Debug.LogError(FAIR_GUARD_ANDROID_JAR_PATH + " 无法找到加固工具, FairGuard加固失败")
  409. return False
  410. with open(FAIR_GUARD_ANDROID_CONFIG_PATH) as file:
  411. lines = file.readlines()
  412. for i in range(len(lines)):
  413. line = lines[i].strip()
  414. if line.startswith("keystore-path="):
  415. certPath = line.replace("keystore-path=", "")
  416. if not certPath or not os.path.exists(os.path.abspath(certPath)):
  417. Debug.LogError(FAIR_GUARD_ANDROID_CONFIG_PATH + " 无法找到加固用的证书, FairGuard加固失败")
  418. return False
  419. Debug.LogSuccess("FairGuard加固开始 " + appPath)
  420. try:
  421. cmds = ["java", "-jar", FAIR_GUARD_ANDROID_JAR_PATH,
  422. "-autoconfig",
  423. "-signapk",
  424. "-inputfile", StringFormat.FormatStringPlatform(appPath)]
  425. res = subprocess.check_output(cmds, shell=False, stderr=STDOUT)
  426. res = StringFormat.FormatStdinString(res)
  427. success = False
  428. lines = res.split("\n")
  429. for i in range(len(lines)):
  430. line = lines[i].strip()
  431. if line.endswith("game protect suceess!"):
  432. success = True
  433. if not success:
  434. Debug.Log(res)
  435. Debug.LogError("")
  436. Debug.LogError("FairGuard加固失败 " + appPath)
  437. return False
  438. except Exception as exc:
  439. Debug.LogError(exc)
  440. Debug.Log("")
  441. Debug.LogError("FairGuard加固失败 " + appPath)
  442. return False
  443. Debug.LogSuccess("FairGuard加固完成 " + appPath)
  444. return True
  445. def ZipCompress(appPath):
  446. appPath = os.path.abspath(appPath + "/../")
  447. if not appPath or appPath == "" or not os.path.exists(StringFormat.FormatStringPlatform(appPath)):
  448. Debug.LogError(appPath + " 无法找到上次打包的App, Zip压缩失败")
  449. return False
  450. fileName = os.path.basename(appPath)
  451. dirPath = os.path.dirname(appPath)
  452. os.chdir(dirPath)
  453. if os.path.exists(fileName + ".zip"):
  454. os.remove(fileName + ".zip")
  455. Debug.LogSuccess("Zip压缩开始 " + appPath)
  456. try:
  457. cmds = ["zip", "-q", "-r",
  458. StringFormat.FormatStringPlatform(fileName + ".zip"),
  459. StringFormat.FormatStringPlatform(fileName)]
  460. res = subprocess.check_output(cmds, shell=False)
  461. except Exception as exc:
  462. Debug.LogError(exc)
  463. Debug.Log("")
  464. Debug.LogError("Zip压缩失败 " + appPath)
  465. return False
  466. Debug.LogSuccess("Zip压缩完成 " + appPath)
  467. return True
  468. def SettingAndroidStudio(packInfo, params):
  469. if not packInfo:
  470. Debug.LogError("设置AndroidStudio时 packInfo 为空")
  471. return False
  472. if not params:
  473. Debug.LogError("设置AndroidStudio时 params 为空")
  474. return False
  475. if not params.has_key(UNITY_TAG_AppOutPath):
  476. Debug.LogError("设置AndroidStudio时 params 不能找到 " + UNITY_TAG_AppOutPath)
  477. return
  478. if not params.has_key(TAG_OTHER_BUILD_PROJECT_PATH):
  479. Debug.LogError("设置AndroidStudio时 params 不能找到 " + TAG_OTHER_BUILD_PROJECT_PATH)
  480. return False
  481. if not params.has_key(UNITY_TAG_ANDROID_SDK_ROOT):
  482. Debug.LogError("设置AndroidStudio时 params 不能找到 " + UNITY_TAG_ANDROID_SDK_ROOT)
  483. return False
  484. if not params.has_key(UNITY_TAG_ANDROID_NDK_ROOT):
  485. Debug.LogError("设置AndroidStudio时 params 不能找到 " + UNITY_TAG_ANDROID_NDK_ROOT)
  486. return False
  487. if not params.has_key(TAG_KEYSTORE_PATH):
  488. Debug.LogError("设置AndroidStudio时 params 不能找到 " + TAG_KEYSTORE_PATH)
  489. return False
  490. if not params.has_key(UNITY_TAG_ICON_PATH):
  491. Debug.LogError("设置AndroidStudio时 params 不能找到 " + UNITY_TAG_ICON_PATH)
  492. return False
  493. appPath = params[UNITY_TAG_AppOutPath].replace('\\', '/')
  494. androidStudioPath = params[TAG_OTHER_BUILD_PROJECT_PATH].replace('\\', '/')
  495. sdkPath = params[UNITY_TAG_ANDROID_SDK_ROOT].replace('\\', '/')
  496. ndkPath = params[UNITY_TAG_ANDROID_NDK_ROOT].replace('\\', '/')
  497. keyStorePath = params[TAG_KEYSTORE_PATH].replace('\\', '/')
  498. iconPath = params[UNITY_TAG_ICON_PATH].replace('\\', '/')
  499. if not os.path.exists(StringFormat.FormatStringPlatform(androidStudioPath)):
  500. Debug.LogError(androidStudioPath + " 无法找到")
  501. return False
  502. if not os.path.exists(StringFormat.FormatStringPlatform(sdkPath)):
  503. Debug.LogError(sdkPath + " 无法找到")
  504. return False
  505. if not os.path.exists(StringFormat.FormatStringPlatform(ndkPath)):
  506. Debug.LogError(ndkPath + " 无法找到")
  507. return False
  508. if not os.path.exists(StringFormat.FormatStringPlatform(keyStorePath)):
  509. Debug.LogError(keyStorePath + " 无法找到")
  510. return False
  511. if not os.path.exists(StringFormat.FormatStringPlatform(iconPath)):
  512. Debug.LogError(iconPath + " 无法找到")
  513. return False
  514. Debug.LogSuccess("设置AndroidStudio 开始 " + androidStudioPath)
  515. localpropPath = androidStudioPath + "/" + "local.properties"
  516. paramMap = {"sdk.dir": sdkPath, "ndk.dir": ndkPath}
  517. if not ChangeFileLineContent(localpropPath, localpropPath, paramMap):
  518. return False
  519. lebianlocalpropPath = androidStudioPath + "/lebian/" + "local.properties"
  520. if not ChangeFileLineContent(lebianlocalpropPath, lebianlocalpropPath, paramMap):
  521. return False
  522. build_custPath = androidStudioPath + "/" + "build_cust.gradle"
  523. idx = appPath.rfind('/')
  524. apkDir = appPath[:idx]
  525. apkName = appPath[idx+1:]
  526. paramMap = {
  527. "appName": '\'' + packInfo['appName'] + '\'',
  528. "applicationId": '\'' + packInfo['bundleId'] + '\'',
  529. "versionCode": str(GetVersionCode(packInfo['gameVersionCode'])),
  530. "versionName": '\'' + GetVersionName(packInfo['gameVersionCode']) + '\'',
  531. "keyFile": '\'' + keyStorePath + '\'',
  532. "keyAlias": '\'' + packInfo['keyaliasName'] + '\'',
  533. "keyPassword": '\'' + packInfo['keyaliasPass'] + '\'',
  534. "storePassword": '\'' + packInfo['keystorePass'] + '\'',
  535. "apkDir": '\'' + apkDir + '\'',
  536. "apkName": '\'' + apkName + '\''
  537. }
  538. if not ChangeFileLineContent(build_custPath, build_custPath, paramMap):
  539. return False
  540. lebian_custPath = androidStudioPath + "/lebian/" + "lebian_cust.gradle"
  541. paramMap = {
  542. "meta_MainChId": '\'' + packInfo['leBian_MainChId'] + '\'',
  543. "meta_ClientChId": '\'' + packInfo['leBian_ClientChId'] + '\'',
  544. "meta_LEBIAN_SECID": '\'' + packInfo['leBian_SECID'] + '\'',
  545. "meta_LBCloudId": '\'' + packInfo['leBian_LBCloudID'] + '\''
  546. }
  547. if not ChangeFileLineContent(lebian_custPath, lebian_custPath, paramMap):
  548. return False
  549. appNameXmlPath = androidStudioPath + '/app/src/main/res/values/strings.xml'
  550. paramMap = {
  551. "<string name=\"app_name\">(.*)</string>": "<string name=\"app_name\">" + packInfo['appName'] + "</string>"
  552. }
  553. if not ChangeFileMatchContent(appNameXmlPath, appNameXmlPath, paramMap):
  554. return False
  555. iconCopyPaths = {
  556. "/app/src/main/res/mipmap-hdpi/app_icon.png" : "/72.png",
  557. "/app/src/main/res/mipmap-hdpi/app_icon_round.png" : "/72.png",
  558. "/app/src/main/res/mipmap-mdpi/app_icon.png" : "/48.png",
  559. "/app/src/main/res/mipmap-mdpi/app_icon_round.png" : "/48.png",
  560. "/app/src/main/res/mipmap-xhdpi/app_icon.png" : "/96.png",
  561. "/app/src/main/res/mipmap-xhdpi/app_icon_round.png" : "/96.png",
  562. "/app/src/main/res/mipmap-xxhdpi/app_icon.png" : "/144.png",
  563. "/app/src/main/res/mipmap-xxhdpi/app_icon_round.png" : "/144.png",
  564. "/app/src/main/res/mipmap-xxxhdpi/app_icon.png" : "/192.png",
  565. "/app/src/main/res/mipmap-xxxhdpi/app_icon_round.png" : "/192.png",
  566. }
  567. for k in iconCopyPaths:
  568. if not CopyFile(iconPath + iconCopyPaths[k], androidStudioPath + k):
  569. return False
  570. ls = GetFilesByFileName(androidStudioPath, ['WXEntryActivity.java'])
  571. if ls and len(ls) > 0:
  572. srcPath = ls[0]
  573. dirPath = GetWXEntryActivityDirPath(srcPath)
  574. if not dirPath:
  575. Debug.LogError(androidStudioPath + " 不存在 WXEntryActivity.java")
  576. return False
  577. destPath = srcPath[:-(len(dirPath) + len('/WXEntryActivity.java'))] + packInfo['bundleId'].replace(".", "/") + "/wxapi/WXEntryActivity.java"
  578. if srcPath != destPath:
  579. paramMap = {
  580. "package (.*);" : "package "+ packInfo['bundleId'] + ".wxapi;"
  581. }
  582. if not ChangeFileMatchContent(srcPath, destPath, paramMap):
  583. return False
  584. for i in range(len(ls)):
  585. if ls[i] != destPath:
  586. PathConvert.DeletePathUtilIsNone(srcPath)
  587. Debug.LogSuccess("设置AndroidStudio 完成 " + androidStudioPath)
  588. return True
  589. def GetWXEntryActivityDirPath(srcPath):
  590. try:
  591. if not os.path.exists(srcPath):
  592. Debug.LogError(srcPath + " 不存在")
  593. return None
  594. with open(srcPath, 'r') as file_obj:
  595. lines = file_obj.readlines()
  596. for i in range(len(lines)):
  597. line = lines[i].strip()
  598. if line.startswith("package"):
  599. idx = line.find(';')
  600. return line[7:idx].strip()
  601. except Exception as exc:
  602. Debug.LogError(exc)
  603. Debug.Log("")
  604. return None
  605. return None
  606. def CopyFile(srcPath, destPath, ingoreCheckDest = False):
  607. try:
  608. if not os.path.exists(srcPath):
  609. Debug.LogError(srcPath + " 不存在")
  610. return False
  611. if not ingoreCheckDest and not os.path.exists(destPath):
  612. Debug.LogError(destPath + " 不存在")
  613. return False
  614. shutil.copyfile(srcPath, destPath)
  615. except Exception as exc:
  616. Debug.LogError(exc)
  617. Debug.Log("")
  618. Debug.LogError(srcPath + " -> " + destPath)
  619. return False
  620. return True
  621. def GetFilesByFileName(path, fileNames = None):
  622. ls = []
  623. if not fileNames:
  624. pass
  625. elif not os.path.exists(path):
  626. pass
  627. elif os.path.isfile(path):
  628. if os.path.basename(path) in fileNames:
  629. ls.append(path.replace("\\", "/"))
  630. elif os.path.isdir(path):
  631. for childPath in os.listdir(path):
  632. children = GetFilesByFileName(os.path.join(
  633. path, childPath), fileNames)
  634. ls.extend(children)
  635. return ls
  636. def ChangeFileMatchContent(srcPath, destPath, paramMap):
  637. try:
  638. if not os.path.exists(StringFormat.FormatStringPlatform(srcPath)):
  639. Debug.LogError(srcPath + " 无法找到")
  640. return False
  641. content = None
  642. with open(srcPath, 'r') as file_obj:
  643. content = file_obj.read()
  644. for k in paramMap:
  645. content = re.sub(k, paramMap[k], content)
  646. if content:
  647. if not os.path.exists(StringFormat.FormatStringPlatform(destPath)):
  648. dirPath = os.path.dirname(StringFormat.FormatStringPlatform(destPath))
  649. if not os.path.exists(dirPath):
  650. os.makedirs(dirPath)
  651. with open(destPath, 'w') as file_obj:
  652. file_obj.write(content)
  653. except Exception as exc:
  654. Debug.LogError(exc)
  655. Debug.Log("")
  656. Debug.LogError(srcPath + " -> " + destPath)
  657. return False
  658. return True
  659. def ChangeFileLineContent(srcPath, destPath, paramMap, match = '='):
  660. try:
  661. if not os.path.exists(StringFormat.FormatStringPlatform(srcPath)):
  662. Debug.LogError(srcPath + " 无法找到")
  663. return False
  664. newLines = []
  665. with open(srcPath, 'r') as file_obj:
  666. lines = file_obj.readlines()
  667. for i in range(len(lines)):
  668. line = lines[i]
  669. stripLine = line.lstrip()
  670. for k in paramMap:
  671. if stripLine.startswith(k):
  672. stripLine = stripLine[len(k):].lstrip()
  673. if stripLine[0] == match:
  674. stripLine = stripLine[1:].lstrip()
  675. idx = line.rfind(stripLine)
  676. line = line[0:idx] + paramMap[k] + '\n'
  677. break
  678. newLines.append(line)
  679. if not os.path.exists(StringFormat.FormatStringPlatform(destPath)):
  680. dirPath = os.path.dirname(StringFormat.FormatStringPlatform(destPath))
  681. if not os.path.exists(dirPath):
  682. os.mkdir(dirPath)
  683. with open(destPath, 'w') as file_obj:
  684. file_obj.write(''.join(newLines))
  685. except Exception as exc:
  686. Debug.LogError(exc)
  687. Debug.Log("")
  688. Debug.LogError(srcPath + " -> " + destPath)
  689. return False
  690. return True
  691. def PackAndroidStudio(packInfo, params):
  692. if not packInfo:
  693. Debug.LogError("打包AndroidStudio时 packInfo 为空")
  694. return False
  695. if not params:
  696. Debug.LogError("打包AndroidStudio时 params 为空")
  697. return False
  698. if not params.has_key(TAG_OTHER_BUILD_PROJECT_PATH):
  699. Debug.LogError("打包AndroidStudio时 params 不能找到 " + TAG_OTHER_BUILD_PROJECT_PATH)
  700. return False
  701. androidStudioPath = params[TAG_OTHER_BUILD_PROJECT_PATH].replace('\\', '/')
  702. if not os.path.exists(StringFormat.FormatStringPlatform(androidStudioPath)):
  703. Debug.LogError(androidStudioPath + " 无法找到")
  704. return False
  705. Debug.LogSuccess("打包AndroidStudio 开始 " + androidStudioPath)
  706. os.chdir(androidStudioPath)
  707. try:
  708. # MAC(OSX)升到 11.0.1 bigsur 后 gradle 无法直接找到 tools.jar
  709. env = os.environ.copy()
  710. if CUR_PLATFORM_NAME == 'Darwin':
  711. cmds = ['/usr/libexec/java_home', '-V']
  712. sp = subprocess.Popen(cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, env=env)
  713. contents = sp.communicate()
  714. sp.wait()
  715. for content in contents:
  716. lines = content.split('\n')
  717. for i in range(len(lines)):
  718. line = lines[i].strip()
  719. if line != '':
  720. path = None
  721. if os.path.exists(line):
  722. path = line + '/lib/tools.jar'
  723. else:
  724. paths = line.split(' ')
  725. for j in range(len(paths)):
  726. if path:
  727. path = path + ' ' + paths[j]
  728. elif os.path.exists(paths[j]):
  729. path = paths[j]
  730. if path and os.path.exists(path):
  731. env['JAVA_HOME'] = path
  732. if CUR_PLATFORM_NAME == 'Windows':
  733. cmds = ['gradlew.bat', "assembleRelease"]
  734. res = subprocess.check_output(cmds, shell=False, env=env)
  735. else:
  736. cmds = ['sh', 'gradlew', "assembleRelease"]
  737. res = subprocess.check_output(cmds, shell=False, env=env)
  738. res = StringFormat.FormatStdinString(res)
  739. success = False
  740. lines = res.split("\n")
  741. for i in range(len(lines)):
  742. line = lines[i].strip()
  743. if line.startswith("BUILD SUCCESSFUL"):
  744. success = True
  745. if not success:
  746. Debug.LogError(res)
  747. Debug.Log('')
  748. Debug.LogError("gradlew assembleRelease Fail ")
  749. return False
  750. except Exception, exc:
  751. Debug.LogError(exc)
  752. Debug.Log("")
  753. Debug.LogError("打包AndroidStudio 失败 " + androidStudioPath)
  754. return False
  755. Debug.LogSuccess("打包AndroidStudio 成功 " + androidStudioPath)
  756. return True
  757. def PackRunOne(unityPath, packId):
  758. os.chdir(PROJECT_PATH)
  759. packInfo, platformCfgName, platformName = GetPackInfo(packId)
  760. if not packInfo or not platformCfgName or not platformName:
  761. Debug.LogError("不是有效的打包平台Id " + packId)
  762. return False, " 不是有效的打包平台Id "
  763. Debug.Log("")
  764. Debug.LogSuccess("开始打包 " + GetPackName(packInfo))
  765. Debug.Log("")
  766. logPath = os.path.abspath(BUILD_LOG_PATH + ("/build_%s.log" % packId))
  767. DeleteUnityLog(logPath)
  768. if not UnityPack(unityPath, packId, logPath):
  769. return False, GetPackName(packInfo) + " Unity打包失败"
  770. _, params = ParseUnityLog(logPath)
  771. appPath = None
  772. if params.has_key(UNITY_TAG_AppOutPath):
  773. appPath = params[UNITY_TAG_AppOutPath]
  774. if platformCfgName == 'weiDuanAndroids':
  775. if not SettingAndroidStudio(packInfo, params):
  776. return False, GetPackName(packInfo) + " 设置AndroidStudio项目失败"
  777. if not PackAndroidStudio(packInfo, params):
  778. return False, GetPackName(packInfo) + " 打包AndroidStudio项目失败"
  779. else:
  780. if "FairGuard" in packInfo["plugins"]:
  781. if platformName.lower() == "android":
  782. if not FairGuardAndroid(appPath):
  783. return False, GetPackName(packInfo) + " FairGuard加固失败"
  784. elif platformName.lower() == "ios":
  785. if not FairGuardiOS(appPath):
  786. return False, GetPackName(packInfo) + " FairGuard加固失败"
  787. if platformName.lower() == "ios":
  788. if not ZipCompress(appPath):
  789. return False, GetPackName(packInfo) + " Zip压缩失败"
  790. Debug.Log("")
  791. Debug.LogSuccess("完成打包 " + GetPackName(packInfo))
  792. Debug.Log("")
  793. return True, GetPackName(packInfo)
  794. def PackRun(unityPath, packIdStr):
  795. packIds = packIdStr.split(':')
  796. errors = []
  797. success = []
  798. for i in range(len(packIds)):
  799. packId = packIds[i]
  800. if packId and not (packId == ""):
  801. res, error = PackRunOne(unityPath, packId)
  802. if not res:
  803. errors.append(packId + "\t" + error)
  804. else:
  805. success.append(packId + "\t" + error)
  806. successNum = len(success)
  807. if successNum > 0:
  808. Debug.Log("")
  809. Debug.Log("")
  810. Debug.Log("")
  811. Debug.LogSuccess("打包成功的有:")
  812. for i in range(successNum):
  813. Debug.LogSuccess(success[i])
  814. else:
  815. Debug.Log("")
  816. Debug.Log("")
  817. errorNum = len(errors)
  818. if errorNum > 0:
  819. Debug.Log("")
  820. Debug.LogError("打包失败的有:", True)
  821. for i in range(errorNum):
  822. Debug.LogError(errors[i], True)
  823. Debug.Log("")
  824. Debug.Log("")
  825. sys.exit(2)
  826. else:
  827. Debug.Log("")
  828. Debug.Log("")
  829. def CleanBuildError():
  830. try:
  831. os.chdir(PROJECT_PATH)
  832. cmds = ["git", "checkout", "./ProjectSettings/ProjectSettings.asset"]
  833. res = subprocess.check_output(cmds, shell=False)
  834. res = StringFormat.FormatStdinString(res)
  835. Debug.Log(res)
  836. PathConvert.DeletePathAll("./Assets/Plugins/Android")
  837. PathConvert.DeletePathUtilIsNoneOnUnityProject("./Assets/Plugins/Android")
  838. PathConvert.DeletePathAll("./Assets/Plugins/iOS")
  839. PathConvert.DeletePathUtilIsNoneOnUnityProject("./Assets/Plugins/iOS")
  840. PathConvert.DeletePathAll("./Assets/Plugins/SDKBridge")
  841. PathConvert.DeletePathUtilIsNoneOnUnityProject("./Assets/Plugins/SDKBridge")
  842. except Exception as exc:
  843. Debug.LogError(exc)
  844. Debug.Log("")
  845. Debug.LogError("解决编译错误失败,请通知程序 ")
  846. def Main():
  847. try:
  848. args = sys.argv[1:]
  849. dic = {}
  850. num = len(args)
  851. for i in range(num):
  852. arg = args[i]
  853. if arg.startswith('-'):
  854. if not arg in VALID_CMD_ARGV:
  855. Debug.LogError(arg + " 不是有效的参数")
  856. PrintHelp()
  857. return
  858. if i + 1 >= num:
  859. Debug.LogSuccess(arg)
  860. dic[arg] = ''
  861. else:
  862. if args[i + 1].startswith('-'):
  863. Debug.LogSuccess(arg)
  864. dic[arg] = ''
  865. else:
  866. Debug.LogSuccess(arg + " " + args[i + 1])
  867. dic[arg] = args[i + 1]
  868. except Exception as e:
  869. Debug.LogError(e)
  870. PrintHelp()
  871. return
  872. if len(dic) <= 0:
  873. PrintHelp()
  874. return
  875. if ('-help' in dic):
  876. PrintHelp()
  877. return
  878. if ('-config' in dic):
  879. PrintConfig(dic['-config'])
  880. return
  881. if (not '-nogit' in dic):
  882. if not ExecGit():
  883. return
  884. unityPath = None
  885. if ('-unitypath' in dic):
  886. unityPath = dic['-unitypath']
  887. unityPath = GetUnityPath(unityPath)
  888. if not ValidUnityPath(unityPath):
  889. return
  890. if not ValidJava():
  891. return
  892. if ('-clrerr' in dic):
  893. CleanBuildError()
  894. packId = None
  895. if ('-buildApp' in dic):
  896. packId = dic['-buildApp']
  897. PackRun(unityPath, packId)
  898. return
  899. if __name__ == '__main__':
  900. try:
  901. Main()
  902. except Exception as e:
  903. Debug.LogError(e)
  904. Debug.LogError(traceback.format_exc(), True)
  905. sys.exit(1)