利用IncrediBuild加速NDK编译
最近一段时间突然发现打包机上编译引擎安卓版本极慢,正好有现成的IncrediBuild所以研究下能不能用来加速。IB本身是商业软件,对Visual Studio的支持已经非常好了: 既可以使用Extension形式直接调用,又可以使用命令行传参sln。但是网上关于结合NDK使用的资料就非常少,这周花了大半天终于跑通流程,就此记录一下。
IB原理及基础使用方式
IncrediBuild User Manual里写的还是比较清楚,我主要看了下BuildConsole Command Line Interface这个部分:
- 支持直接处理VS工程,如
BuildConsole.exe MySln.sln /build
; - 支持将任务以XML形式的信息声明然后调用处理;
- 直接调用某个Command,然后以劫持的形式将任务分布式,如List of Supported Build Tools里列出的
make
,这个稍后详细解释。
ps. 这里面第二个路子最清晰,第三个路子其实是依靠IB自动劫持make中调用clang++的行为;此外IB能够自动复制所需工具及源代码到Agent上执行编译行为。这个行为让我非常眼熟,想起之前研究UE4 Swarm分布式烘焙的时候还好奇过它是怎么跨Agent控制版本的,最后发现就是大力出奇迹的复制exe搞定…
UE4调用方式
出于好奇,就先研究了下UE4是如何调用IB的:在UBT里找到了XGE.cs
包装了相关功能
ProcessStartInfo XGEStartInfo = new ProcessStartInfo( |
这么看来是用的前面说的方法2——以XML形式声明任务并调用。随手建了个空的工程然后编译,查看了下生成的xml内容:
<BuildSet FormatVersion="1"> |
可以看到其实所有的编译任务都被单独组织成clang++
等调用,非常清晰。
现有资料参考
网上搜了下只找到非常少的资料,而且点进去看了下都是复制粘贴的样子。以Incredibuild 加速编译 NDK为例,譬如我本地的NDK目录是在D:\SDK\android-ndk-r21b
下:
- 新建
D:\SDK\android-ndk-r21b\Profile.xml
,内容为
|
- 修改
D:\SDK\android-ndk-r21b\build\ndk-build.cmd
@echo off |
踩坑经验分享
CompareStringA
参考网上现有资料改完之后,直接调用ndk-build.cmd -j200
然而出师不利:
IncrediBuild : Error: Attempt to call unsupported import function
CompareStringA
网上搜了下压根没有可靠信息。暂时没有思路的情况下进行二分定位: 绕过ndk-build.cmd
直接带参调用XGConsole
命令有同样的错; 去掉-j200
之后保持现状; 去掉SHELL=cmd
之后居然就没这个错了。
后来问了下佳神,怀疑这个是IB自己的BUG…传入的/COMMAND
参数里如果带=
就会出这个问题,令人智熄。后来验证了下确实是这样,因为传入NDK-DEBUG=1
也是一模一样的下场。
job server
尝试去掉SHELL=cmd
之后确实IB启动成功,结果还没高兴满半分钟又炸了:
make: INTERNAL: Exiting with 64 jobserver tokens available; should be 1024!
这又是闹啥幺蛾子?搜了下有人遇到过一样的问题 Make (Parallel Jobs) on Windows
I found this Danny Thorpe’s blog entry that explains the problems with parallel make build on Windows. Basically what it suggests is to set the following environment variable first:
set SHELL=cmd.exe
一路跟进去看了下大致的解释,这就很难搞了…如果不设置SHELL=cmd
就无法并行编译,如果设置了就报上一个错。
我一开始试图找除了命令行传参之外有没有其他方法,但是官方文档Choosing the Shell讲的很绝情:
Unlike most variables, the variable
SHELL
is never set from the environment.
既然改不了IB本身,那就只能对make下手了——首先看下自带的make版本信息
D:\SDK\android-ndk-r21b\prebuilt\windows-x86_64\bin>make --version |
OK, 去官网下载对应make 4.2.1,解压之后在Developer Command Prompt for VS 2019里运行build_w32.bat。替换进去发现没啥问题:
D:\SDK\android-ndk-r21b\prebuilt\windows-x86_64\bin>make --version |
强制设置SHELL
翻了下代码发现默认的shell
使用的是sh.exe
,这里直接改成cmd.exe
即可。
--- job.c Sun May 22 04:22:32 2016 |
修复极端情况
替换进去之后结果发现还是一样的问题,简直是白高兴.jpg…这次具体看了下make
里关于job server的实现,本质就是用一个信号量来处理,理应不会出问题才对。多加了几行log看看:
这里就发现违和之处了: IB传入参数应该是-j1024
,但是windows版本使用的是信号量所以不能超过MAXIMUM_WAIT_OBJECTS
即64,所以导致了问题的发生。找到根源之后就好办了:
--- main.c Tue May 31 15:17:26 2016 |
绕开NDK-DEBUG=1
传参
前面提到IB有=
的问题,所以gradle里不能直接在commandLine
那步将这个参数传入。这个问题会比上一个简单很多,使用环境变量的方式传入即可:
environment "NDK_DEBUG", "${NDK_DEBUG}" |
小结
使用修改过的make
终于把IB结合NDK成功跑起来了,不过目前其实还有遗憾——其实目前的修改方法是强行将-j
传入的并行数降到64,更好的做法其实是换一个job server实现从而让IB能够跑的更带感~
顺便IB的调用方式也很有意思: 基于XML的Task形式可读性非常高,一眼就能看明白具体的任务;自动劫持make的方法则是特别方便,虽然出了问题也是绕的我要吐了…