dotnet/runtime 仓库 Mono 运行时构建指南:从环境准备到 Hello World 全流程
【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime
Mono 是 dotnet/runtime 仓库中负责移动端(iOS、Android)、浏览器(WebAssembly)以及 WASI 工作负载的 .NET 运行时实现,其源码位于 src/mono 目录。本文以 Building Mono 官方文档 为主线,完整覆盖从环境准备、运行时与类库协同构建、常用构建参数、特殊平台构建,到生成 NuGet 包与运行 Hello World 示例的全流程,并结合仓库内的构建工程文件(如 mono.proj、Directory.Build.props)与示例代码,帮助读者在本地真正把 Mono 运行时构建起来并跑通第一个程序。
构建前的环境准备
在动手之前,请先确认你的机器满足构建所必需的软件依赖。dotnet/runtime 仓库针对不同操作系统分别维护了环境要求清单,请根据下表选择与当前机器匹配的文档并逐一安装前置依赖:
| Windows | Linux | macOS |
|---|---|---|
| Windows 环境要求 | Linux 环境要求 | macOS 环境要求 |
说明:环境要求中通常包括对应平台的 C/C++ 编译工具链(Windows 上的 VS 组件、Linux/macOS 上的 clang 与 make)、CMake、Python 以及 .NET SDK 等。Mono 运行时本身是原生 C 代码,通过 CMake 构建,因此编译工具链的完整性直接决定构建成败。例如 mono.proj 中会优先选用
$(CppCompilerAndLinker),未指定时默认回退到clang。
核心构建概念:mono 与 libs 的协同构建
要得到一个可用的完整运行时环境,仅仅构建 Mono 本身是不够的——你还需要同时构建 .NET 类库(libraries)。这是因为 Mono 作为运行时实现,必须搭配System.Private.CoreLib以及其余 BCL 程序集才能执行托管代码。
在仓库根目录执行以下命令即可一次性完成"运行时 + 类库"的完整构建:
./build.sh mono+libsWindows 下对应的命令为:
build.cmd mono+libs这里需要特别注意默认构建配置:默认情况下构建的是debug配置,产物位于 debug 输出目录,其中包含断言(asserts)、较少的代码优化,调试起来更友好。如果你需要做性能测量,或者希望测试跑得更快,可以显式切换到 release 配置(该配置不含上述检查项),只需追加参数:
./build.sh mono+libs -configuration release # 简写形式 ./build.sh mono+libs -c release从 src/mono/README.md 可以看到,Mono 与 CoreCLR 使用不同的构建子集(build subset)与宿主配置,因此官方工作流文档(即本文所依据的 Building Mono)是构建 Mono 的权威指引,它覆盖了顶层build.sh/build.cmd帮助文本未展开的配置矩阵(LLVM、AOT、解释器、WASM、移动端等)。
常用构建命令详解
只构建 Mono 运行时
当你已经完成过一次完整构建(mono+libs),后续如果只修改 Mono 相关代码、只想重新构建运行时本身,可以使用:
./build.sh monoWindows 下:
build.cmd mono构建完成后,产品二进制(product binaries)会输出到artifacts/bin/mono/<OS>.<arch>.<flavor>目录,例如 Linux x64 的 debug 构建对应artifacts/bin/mono/Linux.x64.Debug。其中<OS>、<arch>、<flavor>分别表示目标操作系统、架构与构建配置(Debug/Release)。
配合类库测试构建
如果你需要针对 Mono 的改动运行类库测试,或者运行 HelloWorld 示例,则应改用下面的命令(它会把类库一并准备到可测试状态):
./build.sh mono+libs.pretestWindows 下:
build.cmd mono+libs.pretestpretest子集会把类库构建成可供测试宿主(testhost)使用的形式,mono.proj 中即定义了 testhost 与运行时目录的推导逻辑。
跳过 NuGet 还原的增量构建
如果只修改了 Mono 本身、不涉及包依赖变化,可以使用--build跳过 nuget 包的还原以加快构建速度:
./build.sh mono --buildWindows 下:
build.cmd mono --build常用构建参数
构建系统支持通过 MSBuild 属性(/p:Name=value形式)来定制 Mono 的构建行为。以下是官方文档列出的高频参数,结合源码可进一步理解其作用机理:
/p:MonoEnableLLVM=true—— 启用 LLVM 后端
Mono 的 JIT/AOT 编译器可以通过 LLVM 后端生成更高质量的机器码。启用方式:
./build.sh mono /p:MonoEnableLLVM=true从 mono.proj 的头部注释可以看到,MonoEnableLLVM是受支持的构建属性之一,其作用在 mono.proj 中体现为:当启用 LLVM 时会把 LLVM 优化器(llc、opt)一并打包进运行时产物(MonoBundleLLVMOptimizer),并传入 CMake 参数-DLLVM_PREFIX指向 LLVM 安装目录(见 mono.proj)。
/p:MonoLLVMDir=path/to/llvm—— 指定自定义 LLVM 路径
当 LLVM 不在默认位置时,可显式指定:
./build.sh mono /p:MonoEnableLLVM=true /p:MonoLLVMDir=path/to/llvmsrc/mono/Directory.Build.props 的逻辑是:当设置了MonoEnableLLVM或MonoAOTEnableLLVM且未显式提供MonoLLVMDir时,会启用 NuGet 中的 LLVM SDK 包并将MonoLLVMDir归一化到$(MonoObjDir)/llvm;若显式传入了路径,则使用你指定的路径。
/p:MonoLLVMUseCxx11Abi=true—— 适配 C++11 ABI 的 LLVM
如果你的自定义 LLVM 是以 C++11 ABI 编译的,需要追加该参数:
./build.sh mono /p:MonoEnableLLVM=true /p:MonoLLVMDir=path/to/llvm /p:MonoLLVMUseCxx11Abi=true这在某些 Linux 发行版(使用较新 libstdc++ ABI 的场景)下尤为必要,可以避免链接期 ABI 不匹配问题。
/p:DisableCrossgen=true—— 跳过安装器构建
如果你不需要构建安装器(installer),可以跳过 crossgen 步骤以加快构建:
./build.sh mono /p:DisableCrossgen=true/p:KeepNativeSymbols=true—— 保留原生符号便于调试
默认情况下构建会把原生符号剥离到独立文件中;若你需要在 lldb 中直接调试 Mono,可以保留符号:
./build.sh mono /p:KeepNativeSymbols=true该参数在 eng/build.sh 中被实际追加到构建参数列表中,官方文档也明确说明它有助于使用 lldb 调试 Mono。
其他受支持的 Mono 构建属性
除官方文档列出的参数外,mono.proj 的注释还揭示了以下可在/p:中使用的属性,读者可按需组合:
MonoForceInterpreter—— 强制启用解释器(interpreter);MonoAOTEnableLLVM—— 仅对 AOT-only 的 Mono 启用 LLVM;MonoVerboseBuild—— 输出详细构建日志;MonoThreadSuspend—— 线程挂起模式,可选coop、hybrid、preemptive;WasmEnableThreads—— 为 wasm 构建带线程支持的运行时。
例如 mono.proj 中展示了默认挂起模式的选择逻辑:watchOS 与启用线程的 wasm 默认coop,wasm/wasi 默认preemptive(不需要安全点),其余平台默认hybrid。
构建系统还提供大量其他选项,随时可以通过build.sh -?(Windows 为build.cmd -?)查看完整帮助。
特殊平台构建
WebAssembly
Mono 在浏览器场景下以 WebAssembly 为目标平台运行,相关构建与运行说明请参阅 Building WebAssembly。仓库中同时维护了丰富的 wasm 示例(src/mono/sample/wasm),涵盖 console、browser、Blazor frame、事件管道(eventpipe)、线程等场景。
Android
Android 上运行 Mono 的测试方式见 Testing Android。Android 属于交叉编译场景,mono.proj 会为 Android/Bionic 目标启用交叉工具链(MonoUseCrossTool)。
iOS
iOS(含 tvOS、Mac Catalyst 等 Apple 平台)的测试方式见 Testing iOS。Apple 平台同样默认构建 AOT 交叉编译器(见 mono.proj)。
生成 NuGet 包
如需产出 Mono 运行时对应的 NuGet 包,在仓库根目录执行:
./build.sh packs -runtimeFlavor mono # 可附加 -c release 使用 release 配置 ./build.sh packs -runtimeFlavor mono -c releaseWindows 下:
build.cmd packs -runtimeFlavor mono生成的包会出现在artifacts/packages/<configuration>/Shipping目录下,典型产物包括:
Microsoft.NETCore.Runtime.Mono.<version>-dev.<number>.1.nupkgruntime.<OS>.Microsoft.NETCore.Runtime.Mono.<version>-dev.<number>.1.nupkgtransport.Microsoft.NETCore.Runtime.Mono.<version>-dev.<number>.1.nupkgtransport.runtime.<OS>.Microsoft.NETCore.Runtime.Mono.<version>-dev.<number>.1.nupkg
其中transport.*包用于包与包之间的依赖传递场景,runtime.<OS>.*则按操作系统分平台。
上手第一个程序:Hello World
仓库在 src/mono/sample/HelloWorld 提供了一个开箱即用的示例。
示例的入口程序 Program.cs 非常巧妙:它通过检测System.Private.CoreLib程序集中是否存在Mono.RuntimeStructs类型来判断当前跑在哪个运行时上,并打印运行时信息:
bool isMono = typeof(object).Assembly.GetType("Mono.RuntimeStructs") != null; Console.WriteLine($"Hello World {(isMono ? "from Mono!" : "from CoreCLR!")}"); Console.WriteLine(typeof(object).Assembly.FullName); Console.WriteLine(System.Reflection.Assembly.GetEntryAssembly()); Console.WriteLine(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription);从示例目录运行:
cd ../.. make runMakefile 中的逻辑揭示了它的实际执行路径:run目标依赖publish,即先调用仓库顶层的dotnet.sh publish(以-r $(TARGET_OS)-$(MONO_ARCH)指定运行时标识,确保以 SelfContained 方式发布以使用 Mono 而非 CoreCLR),随后直接执行artifacts/bin/HelloWorld/<arch>/<config>/<OS>-<arch>/publish/HelloWorld。
Makefile 还暴露了一组非常有用的开关,供你体验 Mono 的不同特性组合:
MONO_CONFIG(默认Debug)—— 构建配置;MONO_ARCH/TARGET_OS—— 目标架构与操作系统(由仓库的 init-os-and-arch.sh 自动探测);AOT(默认false)—— 是否启用 AOT 预编译(/p:SampleUseAOT);FULL_AOT(默认false)—— 是否启用 Full AOT(会追加--full-aot到MONO_ENV_OPTIONS);TRIM(默认false)—— 是否启用裁剪(/p:SampleTrim);USE_LLVM(默认false)—— 是否启用 LLVM 后端(/p:MonoEnableLLVM);StripILCode/TrimmingEligibleMethodsOutputDirectory—— IL 剥离与裁剪方法输出目录。
对应的 HelloWorld.csproj 中,PublishTrimmed、RunAOTCompilation、SampleAOTMode(normal/full)会根据上述开关联动;当启用 AOT 时,还会通过MonoAOTCompiler任务对发布目录下的所有程序集执行 AOT 编译(见 HelloWorld.csproj),并可选地执行 IL 剥离(ILStrip)。
小贴士:如果希望直接看到 Mono 与 CoreCLR 的输出差异,可以在同一台机器上用
dotnet run(CoreCLR)与make run(Mono)分别运行该示例,Program.cs输出的第一行会明确告诉你当前是哪个运行时。
重要提示与故障排查
官方文档还给出了三点非常实用的提醒:
测试二进制暂未对 Mono 提供(Test binaries are not yet available for mono),因此不要期望
mono子集直接产出测试程序集;如需测试请使用mono+libs.pretest或参考 Testing Mono 文档。构建日志统一放在
artifacts/log目录。当构建失败时,这里是最重要的第一手排查资料,日志中记录了完整的 MSBuild 输出与错误堆栈。构建的所有中间产物都在
artifacts/obj/mono目录。如果你怀疑存在脏状态、希望强制全量重建,删除该目录后重新执行构建命令即可(注意原文档使用remove/rm -rf描述该操作,删除前请自行确认)。
总结
Mono 的构建链路清晰地分为两条主线:mono子集负责原生运行时(含 LLVM、AOT、解释器、交叉编译等配置矩阵),libs(及libs.pretest)负责类库与测试宿主;两者通过mono+libs组合命令协同产出完整运行时环境。掌握本文中的构建命令与/p:参数(MonoEnableLLVM、MonoLLVMDir、DisableCrossgen、KeepNativeSymbols等),再配合 HelloWorld 示例 的make run验证,即可在本地完成从源码到可运行 Mono 应用的全流程闭环,为后续深入 Mono 源码调试与移动端/浏览器端开发打下基础。
【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考