需求

编译自定义版本的 Mono 运行时,可以用来做 DLL 更新。考虑到跨平台编译,因此尝试使用虚拟机 + Linux 方式编译。

环境

  • macOS 10.14.6
  • Virtual Box 6.0.12
  • Ubuntu 18.04.5 LTS Server
  • Android NDK r13b
  • Unity Mono 2018.4 mbe

准备

下载

Unity Mono

Unity Mono 分支 unity-2018.4-mbe 使用 GitHub Zip 方式下载

编译时使用的版本是 unity-2018.4-mbe 当前的最新版本 https://github.com/Unity-Technologies/mono/commit/7d3af3b2665639dbe0d4be714dbc66c93d419f17

Android NDK

Android NDK r13b

Ubuntu

Ubuntu 18.04 LTS

Ubuntu

安装 Ubuntu

创建一个新的虚拟机,使用专家模式

  • 名字 Ubuntu 18.04 LTS,4096MB内存
  • 2TB硬盘 VMDK(虚拟机磁盘)动态分配

设置 | 网络

  • 网卡1 | 端口转发 添加宿主机 2222 到客户机 22 的映射
  • 网卡2 | 启用网络连接 连接方式:桥接网卡

安装

全程默认设置

设置

  • 名字 ubuntu
  • 服务器名字 ubuntu-server
  • 用户名 ubuntu
  • 密码 123456

按空格键勾选 Install OpenSSH Server,不安装其他任何组件,开始安装

安装完成后直接重启,提示推出光盘时忽略直接按回车即可。

SSH 连接虚拟机

1
ssh -p 2222 ubuntu@127.0.0.1

配置 SSH 密钥登录

如果觉得每次登录、传输文件时输入密码麻烦,可以添加一个公钥到虚拟机中

在宿主机中拷贝公钥:

1
pbcopy < ~/.ssh/id_rsa.pub

在虚拟机中执行

1
2
3
4
5
6
cd
mkdir .ssh
touch .ssh/authorized_keys
chmod 700 .ssh
chmod 600 .ssh/authorized_keys
vi .ssh/authorized_keys

把公钥保存到 authorized_keys 中,保存退出即可。

工程文件

传输文件

1
2
3
4
5
scp -P 2222 mono-unity-2018.4-mbe.zip ubuntu@127.0.0.1:~
scp -P 2222 bdwgc-50b55f29ee4783dd5cc01a975e5bcdf8703cb1ee.zip ubuntu@127.0.0.1:~
scp -P 2222 boringssl-3e0770e18835714708860ba9fe1af04a932971ff.zip ubuntu@127.0.0.1:~
scp -P 2222 libatomic_ops-268c37a63b3d77803c7cec6e5b318c015a9a157c.zip ubuntu@127.0.0.1:~
scp -P 2222 android-ndk-r13b-linux-x86_64.zip ubuntu@127.0.0.1:~

切换 root 账户

SSH 连接后切换账户

1
2
ssh -p 2222 ubuntu@127.0.0.1
sudo -i

从这儿开始所有的后续操作都是使用 root 账户进行的。

解压文件

切换当前目录到 root 用户目录 /root,然后执行解压操作将文件解压到 /root 下。

1
2
3
4
5
6
cd
apt install unzip
unzip -q /home/ubuntu/mono-unity-2018.4-mbe.zip
unzip -q /home/ubuntu/bdwgc-50b55f29ee4783dd5cc01a975e5bcdf8703cb1ee.zip
unzip -q /home/ubuntu/boringssl-3e0770e18835714708860ba9fe1af04a932971ff.zip
unzip -q /home/ubuntu/libatomic_ops-268c37a63b3d77803c7cec6e5b318c015a9a157c.zip

移动文件

1
2
3
4
5
6
7
8
rmdir mono-unity-2018.4-mbe/external/bdwgc/
mv bdwgc-50b55f29ee4783dd5cc01a975e5bcdf8703cb1ee/ mono-unity-2018.4-mbe/external/bdwgc

rmdir mono-unity-2018.4-mbe/external/bdwgc/libatomic_ops
mv libatomic_ops-268c37a63b3d77803c7cec6e5b318c015a9a157c/ mono-unity-2018.4-mbe/external/bdwgc/libatomic_ops

rmdir mono-unity-2018.4-mbe/external/boringssl/
mv boringssl-3e0770e18835714708860ba9fe1af04a932971ff/ mono-unity-2018.4-mbe/external/boringssl

验证文件

输出目录内所有文件,确保内部都是正确的文件,而不是只有错误的目录或空目录。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# ls mono-unity-2018.4-mbe/external/bdwgc
allchblk.c    BCC_MAKEFILE  CMakeLists.txt       doc         gc_cpp.cpp      ia64_save_regs_in_stack.s  Makefile.direct  new_hblk.c    pthread_start.c       reclaim.c                tests                 windows-untested
alloc.c       bdw-gc.pc.in  configure.ac         dyn_load.c  gc_dlopen.c     include                    malloc.c         NT_MAKEFILE   pthread_stop_world.c  SMakefile.amiga          thread_local_alloc.c
appveyor.yml  blacklst.c    cord                 extra       gcj_mlc.c       libatomic_ops              mallocx.c        obj_map.c     pthread_support.c     sparc_mach_dep.S         tools
AUTHORS       build         darwin_stop_world.c  finalize.c  gc.mak          m4                         mark.c           OS2_MAKEFILE  ptr_chck.c            sparc_netbsd_mach_dep.s  typd_mlc.c
autogen.sh    ChangeLog     dbg_mlc.c            fnlz_mlc.c  headers.c       mach_dep.c                 mark_rts.c       os_dep.c      README.md             sparc_sunos4_mach_dep.s  WCC_MAKEFILE
backgraph.c   checksums.c   digimars.mak         gc_cpp.cc   heapsections.c  Makefile.am                misc.c           PCR-Makefile  README.QUICK          specific.c               win32_threads.c

# ls mono-unity-2018.4-mbe/external/bdwgc/libatomic_ops
AUTHORS  autogen.sh  ChangeLog  configure.ac  COPYING  doc  m4  Makefile.am  pkgconfig  README.md  src  tests  TODO

# ls mono-unity-2018.4-mbe/external/boringssl
BUILDING.md  CMakeLists.txt  codereview.settings  CONTRIBUTING.md  crypto  decrepit  fuzz  FUZZING.md  include  INCORPORATING.md  LICENSE  PORTING.md  README.md  ssl  STYLE.md  third_party  tool  util

编译

官方文档

建议先参考官方文档了解编译流程,然后再参考下面的流程手动处理。

安装构建软件

直接执行命令会提示有依赖软件未安装

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# ./autogen.sh --prefix=$PREFIX

**Error**: You must have `autoconf' installed to compile Mono.
Download the appropriate package for your distribution,
or get the source tarball at ftp://ftp.gnu.org/pub/gnu/

**Error**: You must have `libtoolize' installed to compile Mono.
Get ftp://ftp.gnu.org/gnu/libtool/libtool-1.2.tar.gz
(or a newer version if it is available)

**Error**: You must have `automake' installed to compile Mono.
Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz
(or a newer version if it is available)

configure: error: "cmake not found"

需要安装官方文档上指明的依赖软件

1
apt-get install git autoconf libtool automake build-essential gettext cmake python3 curl

初始化编译环境

所有的命令行输出中只截取了最后的一段输出,中间的省略了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# cd mono-unity-2018.4-mbe
# mkdir /opt/mono
# PREFIX=/opt/mono
# echo $PREFIX
/opt/mono
# ./autogen.sh --prefix=$PREFIX

=== configuring in external/bdwgc (/root/mono-unity-2018.4-mbe/external/bdwgc)
configure: WARNING: no configuration information is in external/bdwgc

        mcs source:    mcs
    C# Compiler:   roslyn

   Engine:
    Host:           x86_64-pc-linux-gnu
    Target:           x86_64-pc-linux-gnu
    GC:           sgen (concurrent by default) and Included Boehm GC with typed GC and parallel mark
    TLS:           __thread
    SIGALTSTACK:   no
    Engine:        Building and using the JIT
    BigArrays:     no
    DTrace:        no
    LLVM Back End: no (dynamically loaded: no)

   Libraries:
    .NET 4.x:        yes
    Xamarin.Android: no
    Xamarin.iOS:     no
    Xamarin.WatchOS: no
    Xamarin.TVOS:    no
    Xamarin.Mac:     no
    Windows AOT:     no
    Unity JIT:       default
    Unity AOT:       default
    Orbis:           no
    Unreal:          no
    WebAssembly:     no
    Test profiles:   AOT Full (no), AOT Hybrid (no)
    JNI support:     IKVM Native
    libgdiplus:      assumed to be installed
    zlib:
    BTLS:            yes (x86_64)


Now type `make' to compile

说明大部分条件已准备好,但是缺失 bdwgc 配置文件,实际 bdwgc 目录是空的。

打开构建脚本,注释掉不用的平台 armv5 armv6_vfp,加快构建时间。

1
vi external/buildscripts/build_runtime_android.pl
1
2
3
4
5
# By default, build runtime for all the variants we need.  But allow something to specify an individual variation to build
if ($androidArch eq "")
{
        # system("perl", "$buildScriptsRoot/build.pl", "--build=1", "--clean=1", "--artifact=1", "--arch32=1", "--androidarch=armv5", "--forcedefaultbuilddeps=1", "--windowssubsystemforlinux=$windowsSubsystemForLinux", "--stevedorebuilddeps=$stevedoreBuildDeps") eq 0 or die ("Failed building mono for armv5\n");
        # system("perl", "$buildScriptsRoot/build.pl", "--build=1", "--clean=1", "--artifact=1", "--arch32=1", "--androidarch=armv6_vfp", "--forcedefaultbuilddeps=1", "--windowssubsystemforlinux=$windowsSubsystemForLinux", "--stevedorebuilddeps=$stevedoreBuildDeps") eq 0 or die ("Failed building mono for armv6_vfp\n");

第一次编译

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# sh external/buildscripts/build_runtime_android.sh
>>> PATH in Build All = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin

>>> Build All Args = --build=1 --clean=1 --artifact=1 --arch32=1 --androidarch=armv7a --forcedefaultbuilddeps=1 --windowssubsystemforlinux=0 --stevedorebuilddeps=1
>>> Mono checkout = /root/mono-unity-2018.4-mbe
>> System Info :
Linux ubuntu-server 4.15.0-117-generic #118-Ubuntu SMP Fri Sep 4 20:02:41 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
fatal: not a git repository (or any of the parent directories): .git
fatal: not a git repository (or any of the parent directories): .git
>>> Mono Revision =
>>> Build Scripts Revision =
>>> External build deps = /root/mono-unity-2018.4-mbe/external/buildscripts/artifacts/Stevedore
>>> Existing Mono =
>>> Mono Arch = i686
>>> No existing mono supplied.  Checking for external...
>>> Running bee to download build-deps...
./bee: 5: ./bee: mono: not found
^Cfailed to run bee
Failed building mono for armv7a

安装 Mono

必须先安装 Mono 才能编译 Mono,这是一个自举问题。

编译安装 Mono

虽然 Unity 官方文档中提到了编译安装 monolite,但是速度实在是太慢了

1
2
# Run the following line after ./autogen.sh
make get-monolite-latest

包管理器 Mono

不要使用 apt install mono-devel 安装 ubuntu 包管理器的 mono,无法运行 bee,提示

1
2
3
4
>>> Running bee to download build-deps...

Unhandled Exception:
System.TypeLoadException: Could not load type 'Bee.StandaloneBeeDriver' from assembly 'bee, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.

如果安装了需要先删除

1
sudo apt-get --purge remove mono-runtime

Mono 官方预编译版本

Mono 官方的源太慢,需要使用代理

1
vi /etc/apt/apt.conf.d/proxy.conf

修改代理地址为局域网内代理

1
2
3
4
Acquire {
  HTTP::proxy "http://192.168.1.1:7890";
  HTTPS::proxy "http://192.168.1.1:7890";
}

必须使用代理下载安装 Mono,否则安装极慢

Ubuntu 18.04 (i386, amd64, armhf, arm64, ppc64el)

1
2
3
4
5
sudo apt install gnupg ca-certificates
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
sudo apt update
apt install mono-devel

第二次编译

1
2
3
4
5
6
7
# sh external/buildscripts/build_runtime_android.sh

>>> Android NDK needs to be extracted
>>> Android NDK Extension = .zip
unzip:  cannot find or open /root/mono-unity-2018.4-mbe/external/buildscripts/artifacts/Stevedore/android-ndk-r13b-linux/android-ndk-r13b-linux-x86_64.zip, /root/mono-unity-2018.4-mbe/external/buildscripts/artifacts/Stevedore/android-ndk-r13b-linux/android-ndk-r13b-linux-x86_64.zip.zip or /root/mono-unity-2018.4-mbe/external/buildscripts/artifacts/Stevedore/android-ndk-r13b-linux/android-ndk-r13b-linux-x86_64.zip.ZIP.
Something went wrong with the NDK extraction
Failed building mono for armv7a

需要将 ndk 移动到指定位置

1
2
3
mv /home/ubuntu/android-ndk-r13b-linux-x86_64.zip .
mkdir /root/mono-unity-2018.4-mbe/external/buildscripts/artifacts/Stevedore/android-ndk-r13b-linux
mv android-ndk-r13b-linux-x86_64.zip /root/mono-unity-2018.4-mbe/external/buildscripts/artifacts/Stevedore/android-ndk-r13b-linux/android-ndk-r13b-linux-x86_64.zip

第三次编译

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# sh external/buildscripts/build_runtime_android.sh

>>> LIBTOOLIZE before Build = /root/mono-unity-2018.4-mbe/external/buildscripts/artifacts/Stevedore/built-tools/bin/libtoolize
>>> LIBTOOL before Build = /root/mono-unity-2018.4-mbe/external/buildscripts/artifacts/Stevedore/built-tools/bin/libtool
>>> Cleaning /root/mono-unity-2018.4-mbe/tmp

>>> Calling autogen in mono


>>> Configure parameters are : --disable-mcs-build --with-glib=embedded --disable-nls --disable-btls --with-mcs-docs=no --prefix=/root/mono-unity-2018.4-mbe/tmp --host=armv5-linux-androideabi --disable-parallel-mark --disable-shared-handles --with-sigaltstack=no --with-tls=pthread --disable-visibility-hidden mono_cv_uscore=yes


**Error**: You must have `libtoolize' installed to compile Mono.
Get ftp://ftp.gnu.org/gnu/libtool/libtool-1.2.tar.gz
(or a newer version if it is available)
failing autogenning mono at /root/mono-unity-2018.4-mbe/external/buildscripts/build.pl line 1344.
Failed building mono for armv7a

修改 /root/mono-unity-2018.4-mbe/external/buildscripts/build.pl

1
2
3
4
5
6
7
8
9
# vi /root/mono-unity-2018.4-mbe/external/buildscripts/build.pl

 450                 $ENV{'LIBTOOLIZE'} = "$builtToolsDir/bin/libtoolize";
 451                 $ENV{'LIBTOOL'} = "$builtToolsDir/bin/libtool";

改为

 450                 $ENV{'LIBTOOLIZE'} = "/usr/bin/libtoolize";
 451                 $ENV{'LIBTOOL'} = "/usr/bin/libtool";

第四次编译

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# sh external/buildscripts/build_runtime_android.sh

python ./genmdesc.py TARGET_ARM . cpu-arm.h arm_cpu_desc ./cpu-arm.md
/bin/bash: python: command not found
Makefile:3594: recipe for target 'cpu-arm.h' failed
make[3]: *** [cpu-arm.h] Error 127
make[3]: Leaving directory '/root/mono-unity-2018.4-mbe/mono/mini'
Makefile:481: recipe for target 'all-recursive' failed
make[2]: *** [all-recursive] Error 1
make[2]: Leaving directory '/root/mono-unity-2018.4-mbe/mono'
Makefile:555: recipe for target 'all-recursive' failed
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory '/root/mono-unity-2018.4-mbe'
Makefile:485: recipe for target 'all' failed
make: *** [all] Error 2
Failed to make
Failed building mono for armv7a

提示缺少 Python2,手动安装:

1
apt install python

第五次编译

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# sh external/buildscripts/build_runtime_android.sh

最终提示

>>> Skipping make install.  We don't need to run this step when building the runtime on non-desktop platforms.
>>> Skipping build Unity Script and Boo
>>> Creating artifact...
>>> Creating embedruntimes directory : /root/mono-unity-2018.4-mbe/builds/embedruntimes/android/x86
>>> Copying libmonosgen-2.0.so
>>> Copying libmonobdwgc-2.0.so
>>> Copying libMonoPosixHelper.so
>>> Creating monodistribution directory
>>> Creating version file : /root/mono-unity-2018.4-mbe/builds/versions-android-x86.txt
>>> Skipping unit tests

证明编译成功,检查构建结果

检查

确认编译的目标平台目录里都存在需要的库文件:

1
2
3
4
# ls /root/mono-unity-2018.4-mbe/builds/embedruntimes/android/x86
libmonobdwgc-2.0.so  libMonoPosixHelper.so  libmonosgen-2.0.so
# ls /root/mono-unity-2018.4-mbe/builds/embedruntimes/android/armv7a/
libmonobdwgc-2.0.so  libMonoPosixHelper.so  libmonosgen-2.0.so

把所有文件拷贝到 Gradle 工程中输出使用

注意

不要按下面的方法修改 libbdwgc_libs,会导致编译出来的 mono 无法运行,Unity 运行时在屏幕中间提示

1
2
Error
Failed to load Mono.
1
2
3
4
5
5,修改编译文件错误                                                                   
打开 mono/mini目录下的 makefile文件,找到
libbdwgc_libs = $(monodir)/external/bdwgc/libgc_static.la
改为
libbdwgc_libs = $(monodir)/external/bdwgc/libgc.la

参考资料