基于Python的构建脚本(续)
创建C#命令行打包所需要的配置文件
在
基于Python和Jenkins的Unity自动化打包方案总结(2)
中,我们给C#端的命令行打包接口设计了使用json配置文件接收打包参数的方案。该方案的好处是不需要繁琐的解析很多Unity命令行参数,且添加打包参数更加方便且不易出错。而该方案需要我们生成一个json配置文件,也就是这儿python脚本接下来要做的事情。先上代码:
def main()
json_path = f'{output_folder}/buildConfig.json'
print(f'Creating config json to {json_path}')
build_config = {}
if BUILD_TYPE == 'PC':
build_config['OS'] = 0
elif BUILD_TYPE == 'Android':
build_config['OS'] = 3
build_config['Store'] = 0
build_config['VersionBase'] = VERSION_NO_BASE
build_config['SvnReversion'] = SVN_VERSION_NO
build_config['CleanAssetsCache'] = True
build_config['VersionType'] = VERSION_TYPE
build_config['BuildDateTime'] = BUILD_TIMESTAMP
build_config['OutputFolder'] = output_folder
build_config['DevelopmentMode'] = DEBUG_MODE
build_config['AppName'] = APP_NAME
build_config['BuildScenes'] = BUILD_SCENES
with open(json_path, 'w', encoding='utf-8') as file_obj:
json.dump(build_config, file_obj, indent=4, ensure_ascii=False)
首先,我们确定这个json文件的路径json_path
,也是放到我们的打包输出目录中。
然后,构建一个build_config
字典,该字典的key就是参数名,value为参数值。该字典可以直接dump到json文件中。
根据我们上面获取到的jenkins打包参数,来填充字典。需要说明的是,jenkins参数和C#端的打包参数不一定是一一对应的,比如这儿的BUILD_TYPE
jenkins参数,对应到C#端则是OS
参数,这所以这么做,是因为C#端的参数可能更细致,而Jenkins端为了避免选择太多的参数,而可能将一个参数进行"打包"。比如这儿的BUILD_TYPE
,当值是'PC'时,只是打包一个普通的PC版本,因此只是设置C#端的OS
为对应的枚举值。当如果我们打包一个Steam商店版本,那么BUILD_TYPE
可以设置为Steam
,这样对应的C#参数不光是OS
,还包含Store
参数为Steam商店对应的枚举值。我们给出的例子里面并没有实现Steam商店,因此这儿的Store
参数就统一设置为默认值了。总之,思路就是根据业务需求,设计Jenkins端的参数,并转换成C#端的参数,保存到字典中。
将build_config字典输出到json文件中。这儿我们使用了json
模块的dump
方法,当然首先我们要打开文件,获得文件句柄。dump方法的indent
参数大于0时,可以获得有缩进格式的json,方便我们查看,ensure_ascii
参数设置为False,可以包含中文字符。
执行Unity批处理模式进行打包
终于,万事俱备,可以执行Unity命令行打包了。根据之前的介绍,打包被分为多遍,以应对域切换可能造成的level 0崩溃问题。我们这儿由于没有assets编译,因此只分为两遍。
命令行log输出问题
由于我们是在一个Python脚本中进行处理所有的构建步骤,而每个步骤我们都会输出log,这些log最终在Jenkins的控制台可以看到。但如果我们使用os.system
执行控制台命令,就会阻塞log的输出。因此在执行命令行之前,需要flush一下:
def main()
print('Start Unity building, please wait...')
sys.stdout.flush()
执行Unity命令行
Unity命令行打包的参数之前已经说明过了,本方案我们只需要传入一个自定义参数,即-configFilePath
传入我们刚刚生成的json文件的路径。此外就是Unity需要的参数了,比如-projectPath
和-logFile
。这个logFile参数是Unity自己输出的log文件,这个文件有助于分析打包的过程以及失败原因。且每次执行Unity命令行都要指定一个独立的文件,避免互相覆盖。
def main()
proj_path = WORKSPACE
log_path_1 = f'{log_path}_1.txt'
exe_result = os.system(f'"{unity}" -batchmode -nographics -executeMethod BuilderCommandline.Build_SwitchEnv -projectPath {proj_path} -logFile {log_path_1} -configFilePath:{json_path}')
if exe_result != 0:
print(f'call Unity Build_SwitchEnv failed. result={exe_result}')
exit(1)
log_path_2 = f'{log_path}_2.txt'
exe_result = os.system(f'"{unity}" -batchmode -quit -nographics -executeMethod BuilderCommandline.Build_MakePackage -projectPath {proj_path} -logFile {log_path_2} -configFilePath:{json_path}')
if exe_result != 0:
print(f'call Unity Build_MakePackage failed. result={exe_result}')
exit(1)
print("Build Successful!")
这儿我们对于os.system
的返回结果进行分析,如果不为0,则表示Unity打包失败,此时我们需要使用exit(1)
退出python构建脚本,这将触发Jenkins构建job的失败,从而发送失败邮件。如果一切正常,我们就可以继续最后的步骤了。
拷贝版本到共享目录
打包成功之后,我们往往需要将版本包拷贝到一个共享目录,方便大家去下载测试。这样可以避免Jenkins服务器的访问需求,毕竟这个服务器是比较重要的,应该只有管理员可以访问。这儿直接给出main函数最后的一部分代码:
def main()
if PACKAGE_SHARED_FOLDER=='':
print("Warning: Shared folder is empty, skip copy to shared.")
exit(1)
package_name = f'{APP_NAME}_{VERSION_TYPE}_{BUILD_TIMESTAMP}_{SVN_VERSION_NO}'
if BUILD_TYPE == 'PC':
package_name += '.zip'
elif BUILD_TYPE == 'Android':
package_name += '.apk'
package_path = f'{output_folder}/{package_name}'
package_path = package_path.replace('/','\\')
print(f'Copy package {package_path} to shared folder: {PACKAGE_SHARED_FOLDER}')
sys.stdout.flush();
exe_result = os.system(f'net use {PACKAGE_SHARED_FOLDER} "xxx" /user:"xxx"')
if exe_result != 0:
print(f'net use shared folder failed. result={exe_result}')
exit(1)
exe_result = os.system(f'xcopy /s /y {package_path} {PACKAGE_SHARED_FOLDER}')
if exe_result != 0:
print(f'xcopy to shared folder failed. result={exe_result}')
exit(1)
print('Copy package to shared folder successful! All done!')
共享文件夹的位置也是在Jenkins里面配置的,我们需要使用net use
命令去获取该文件夹的权限,这儿需要使用用户名和密码。然后使用xcopy
命令进行拷贝。如果成功,python脚本就正常退出,Jenkins那边就会判断构建成功,发送成功邮件。
本方案到这儿就总结完毕了。但实际上,还是有可以改进和扩展的地方。比如包名称,现在是在C#和python里面分别拼出来的,这其实应该在Jenkins参数里面给出拼接规则,然后从配置文件传入到C#里面。这样更灵活也避免出错。另外打包之后,现在只是简单的copy到局域网一个共享目录中。对于apk或者ipa来说,最好再实现一个OTA服务器,直接将版本包自动部署到OTA服务器上,在邮件中给出OTA下载链接,这样就可以直接下载安装了。