dotnet/runtime 仓库 Mono 运行时构建指南:从环境准备到 Hello World 全流程
2026/9/20 4:02:48 网站建设 项目流程

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 仓库针对不同操作系统分别维护了环境要求清单,请根据下表选择与当前机器匹配的文档并逐一安装前置依赖:

WindowsLinuxmacOS
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+libs

Windows 下对应的命令为:

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 mono

Windows 下:

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.pretest

Windows 下:

build.cmd mono+libs.pretest

pretest子集会把类库构建成可供测试宿主(testhost)使用的形式,mono.proj 中即定义了 testhost 与运行时目录的推导逻辑。

跳过 NuGet 还原的增量构建

如果只修改了 Mono 本身、不涉及包依赖变化,可以使用--build跳过 nuget 包的还原以加快构建速度:

./build.sh mono --build

Windows 下:

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 优化器(llcopt)一并打包进运行时产物(MonoBundleLLVMOptimizer),并传入 CMake 参数-DLLVM_PREFIX指向 LLVM 安装目录(见 mono.proj)。

/p:MonoLLVMDir=path/to/llvm—— 指定自定义 LLVM 路径

当 LLVM 不在默认位置时,可显式指定:

./build.sh mono /p:MonoEnableLLVM=true /p:MonoLLVMDir=path/to/llvm

src/mono/Directory.Build.props 的逻辑是:当设置了MonoEnableLLVMMonoAOTEnableLLVM且未显式提供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—— 线程挂起模式,可选coophybridpreemptive
  • 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 release

Windows 下:

build.cmd packs -runtimeFlavor mono

生成的包会出现在artifacts/packages/<configuration>/Shipping目录下,典型产物包括:

  • Microsoft.NETCore.Runtime.Mono.<version>-dev.<number>.1.nupkg
  • runtime.<OS>.Microsoft.NETCore.Runtime.Mono.<version>-dev.<number>.1.nupkg
  • transport.Microsoft.NETCore.Runtime.Mono.<version>-dev.<number>.1.nupkg
  • transport.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 run

Makefile 中的逻辑揭示了它的实际执行路径: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-aotMONO_ENV_OPTIONS);
  • TRIM(默认false)—— 是否启用裁剪(/p:SampleTrim);
  • USE_LLVM(默认false)—— 是否启用 LLVM 后端(/p:MonoEnableLLVM);
  • StripILCode/TrimmingEligibleMethodsOutputDirectory—— IL 剥离与裁剪方法输出目录。

对应的 HelloWorld.csproj 中,PublishTrimmedRunAOTCompilationSampleAOTModenormal/full)会根据上述开关联动;当启用 AOT 时,还会通过MonoAOTCompiler任务对发布目录下的所有程序集执行 AOT 编译(见 HelloWorld.csproj),并可选地执行 IL 剥离(ILStrip)。

小贴士:如果希望直接看到 Mono 与 CoreCLR 的输出差异,可以在同一台机器上用dotnet run(CoreCLR)与make run(Mono)分别运行该示例,Program.cs输出的第一行会明确告诉你当前是哪个运行时。

重要提示与故障排查

官方文档还给出了三点非常实用的提醒:

  1. 测试二进制暂未对 Mono 提供(Test binaries are not yet available for mono),因此不要期望mono子集直接产出测试程序集;如需测试请使用mono+libs.pretest或参考 Testing Mono 文档。

  2. 构建日志统一放在artifacts/log目录。当构建失败时,这里是最重要的第一手排查资料,日志中记录了完整的 MSBuild 输出与错误堆栈。

  3. 构建的所有中间产物都在artifacts/obj/mono目录。如果你怀疑存在脏状态、希望强制全量重建,删除该目录后重新执行构建命令即可(注意原文档使用remove/rm -rf描述该操作,删除前请自行确认)。

总结

Mono 的构建链路清晰地分为两条主线:mono子集负责原生运行时(含 LLVM、AOT、解释器、交叉编译等配置矩阵),libs(及libs.pretest)负责类库与测试宿主;两者通过mono+libs组合命令协同产出完整运行时环境。掌握本文中的构建命令与/p:参数(MonoEnableLLVMMonoLLVMDirDisableCrossgenKeepNativeSymbols等),再配合 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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询