MonoDevelop 编译用户应用程序时调用的是哪个编译器

MonoDevelop 编译用户应用程序时调用的是哪个编译器

分析对象:本工作区自编译的 MonoDevelop 7.8.4(monodevelop/main/build/bin)+ Mono 6.12 + MSBuild 16.10.1。

目标:回答"在 IDE 里构建自己写的 C# 应用时,最终由哪个编译器、以什么路径、经什么链路被调用"。


一、结论速览

场景 实际使用的编译器 说明
经典 C# 项目(.csproj + Microsoft.CSharp.targets,含 Gtk# 项目) Roslyn C# 编译器 csc 由 MSBuild 的 Csc 任务驱动,来自 $(RoslynTargetsPath)
.NET Core / .NET Standard SDK 风格项目 Roslyn C# 编译器 csc 仍是同一套 MSBuild + Roslyn,只是把 MSBuildSDKsPath 指向 .NET SDK 的 Sdks
VB.NET 项目 Roslyn VB 编译器 vbc 与 C# 完全对称,Vbc 任务
编辑器内的语法/语义分析、IntelliSense、错误波浪线 进程内 Roslyn 库 (Microsoft.CodeAnalysis.CSharp) 不产出程序集,与构建编译器是两套使用方式
旧的 mcs.exe(Mono C# 编译器) 默认不使用 仅当显式设置 CscToolExe=mcs.exe 才会被选中

一句话:MonoDevelop 本身不直接调用编译器;它启动 MSBuild 构建器,由 MSBuild 的 Csc/Vbc 任务调用 Roslyn 编译器(csc/vbc 或 Roslyn 编译服务器 VBCSCompiler)。


二、完整调用链

复制代码
用户在 IDE 中点击"生成"
        │
        ▼
MonoDevelop.Ide  (Build 命令 / DotNetProject.DoBuild)
        │  选择项目对应的 TargetRuntime(决定用哪一个 Mono)
        ▼
MonoDevelop.Projects.MSBuild(MonoDevelop.Core)
        │  RemoteBuildEngineManager.GetRemoteProjectBuilder(...)
        │  1) 把 <runtime>/lib/mono/msbuild/15.0/bin 整个目录复制到
        │     ~/.cache/MonoDevelop/7.0/MSBuild/<pid>_<runtimeId>/
        │  2) 并把 MonoDevelop.MSBuildBuilder.exe 复制过去作为构建器
        ▼
MonoDevelop.MSBuildBuilder.exe   (由目标运行时 <runtime>/bin/mono 启动)
        │  进程内 Microsoft.Build.Execution.BuildManager
        │  以全局属性初始化:
        │    BuildingInsideVisualStudio = true
        │    UseHostCompilerIfAvailable = false
        │    RoslynTargetsPath = <binDir>/Roslyn
        ▼
加载项目 → 导入 $(MSBuildBinPath)/Microsoft.CSharp.targets
        │   └─ Microsoft.CSharp.CurrentVersion.targets
        │        └─ $(RoslynTargetsPath)/Microsoft.CSharp.Core.targets
        ▼
CoreCompile 目标中的 <Csc ... /> 任务
        │  UsingTask 声明在 Microsoft.Common.tasks:
        │  AssemblyFile="$(RoslynTargetsPath)\Microsoft.Build.Tasks.CodeAnalysis.dll"
        ▼
Microsoft.CodeAnalysis.BuildTasks.Csc(ToolTask)
        │  UseSharedCompilation 默认为 true
        ├── 复用/启动 Roslyn 编译服务器 VBCSCompiler.exe(常驻进程)
        └── 或直接执行 $(RoslynTargetsPath)/csc.exe
        ▼
Roslyn 编译器产出 obj/.../<Assembly>.exe|dll  → 复制到 bin/...

关键点:MonoDevelop 不做任何"编译器选择"决策 。它只负责"用哪个 MSBuild 目录 + 设好全局属性",真正的编译器由 MSBuild targets 里的 Csc/Vbc 任务决定,而这个任务指向 Roslyn。


三、决定"用哪个编译器"的四个关键属性

属性 取值来源 作用
ToolsVersion 写死为 "15.0"(MSBuildProjectService.cs:60) 决定使用 <runtime>/lib/mono/msbuild/15.0/bin,与 csproj 里的 ToolsVersion="4.0" 无关
MSBuildBinPath MonoTargetRuntime.GetMSBuildBinPath("15.0")(MonoTargetRuntime.cs:180-198)→ <mono前缀>/lib/mono/msbuild/15.0/bin 决定构建器复制哪一套 MSBuild,以及 $(MSBuildBinPath) 的解析结果
RoslynTargetsPath 由 IDE 作为全局属性 传入:Path.Combine(binDir, "Roslyn")(RemoteBuildEngineManager.cs:404-434) 决定 Microsoft.CSharp.Core.targets 与 Csc 任务程序集从哪个 Roslyn 目录加载
BuildingInsideVisualStudio / UseHostCompilerIfAvailable IDE 固定传入 true / false 让 targets 按 IDE 构建语义运行,且不使用宿主内编译器

补充:csproj 中 ToolsVersion="4.0" 只是项目文件格式版本,注释在 MSBuildFileFormat.cs 中已说明------"构建器总是使用最新的 tools version"。

用哪个 Mono,取决于 IDE 自己的运行时

TargetRuntime 由 MonoDevelop 探测得到,默认就是它自己正在运行的那个 Mono:

启动方式 目标运行时 MSBuild 目录 编译器实体
双击 MonoDevelop.exe(系统 mono) 系统 Mono 6.12.0.200 /usr/lib/mono/msbuild/15.0/bin /usr/lib/mono/4.5/csc.exe(Roslyn 3.9.0-6.21124.20)
./build-from-source.sh run(自编译 mono) mono-install 的 Mono 6.12.0 mono-install/lib/mono/msbuild/15.0/bin mono-install/lib/mono/4.5/csc.exe(Roslyn 3.9.0-5.21112.8)

msbuild/15.0 在 Mono 安装里是 Current 的符号链接,所以日志中两种路径都可能出现。


四、编译器本体与语言版本

4.1 Roslyn csc / vbc

  • $(RoslynTargetsPath)/csc.exe 在 Mono 中是符号链接:
    .../msbuild/15.0/bin/Roslyn/csc.exe -> ../../../../4.5/csc.exe

  • 版本实测:

    $ mono /usr/lib/mono/4.5/csc.exe -version
    3.9.0-6.21124.20 (db94f4cc)

    $ mono /lib/mono/4.5/csc.exe -version
    3.9.0-5.21112.8 (f3ff0437)

  • mcs.exe 仍在 <mono>/lib/mono/4.5/ 中,但 Microsoft.CSharp.Mono.targets 只在
    CscToolExe == mcs.exe 时才把 CscToolPath 指向框架目录;MonoDevelop 未设置该属性,
    因此默认不会走 mcs。

4.2 编译方式:共享编译(VBCSCompiler)

Microsoft.Managed.Core.targets 中 UseSharedCompilation 默认 true,Csc 任务会把编译请求

交给常驻的 Roslyn 编译服务器:

复制代码
mono <cache>/MSBuild/<pid>_1/Roslyn/VBCSCompiler.exe -pipename:...

首次构建后该进程会存活,后续编译直接复用以加速。若设置 UseSharedCompilation=false,

则退化为每次直接启动 csc.exe。

4.3 语言版本

Microsoft.CSharp.Core.targets 按目标框架推导 _MaxSupportedLangVersion:

目标框架 默认 LangVersion
.NET Framework / .NET Standard < 2.1 7.3
.NET Core < 3.0 7.3
.NET Core < 5.0 或 .NET Standard 2.1 8.0
.NET Core 5.0 9.0

实测 net47 项目的命令行带 /langversion:7.3。需要更高版本时在 csproj 中显式设置

<LangVersion>latest</LangVersion>。


五、实测证据

用与 IDE 相同的构建栈(mdtool → MonoDevelop.MSBuildBuilder.exe)构建一个 Gtk# 项目,

日志中 CoreCompile 目标下直接打印出编译器命令行(节选):

复制代码
目标 CoreCompile:
    /usr/lib/mono/msbuild/Current/bin/Roslyn/csc.exe /noconfig /nowarn:1701,1702,2008
      /fullpaths /nostdlib+ /platform:x86 /define:DEBUG /debug+ /debug:full /optimize-
      /reference:/usr/lib/mono/4.7-api/mscorlib.dll
      /reference:.../gtk-sharp.dll ...
      /out:"obj/x86/Debug/123.exe" /target:winexe /langversion:7.3
      Program.cs MainWindow.cs Properties/AssemblyInfo.cs ...

可确认:

  1. 编译器可执行文件来自 $(RoslynTargetsPath),即 MSBuild 的 Roslyn 目录;
  2. 引用程序集来自目标框架的引用程序集目录(4.7-api)与项目引用;
  3. 编译器由 Microsoft.Build.Tasks.CodeAnalysis.dll 中的 Csc 任务加载。

六、SDK 风格项目(.NET Core / .NET Standard)

  • 不是 调用 dotnet build。仍然走上面同一条链路:Mono 的 MSBuild 15 + 构建器进程。
  • IDE 通过全局属性 MSBuildSDKsPath 指向已安装 .NET SDK 的 Sdks 目录
    (MSBuildSdksPathGlobalPropertyProvider / DotNetCoreSdk.MSBuildSDKsPath);
    若 SDK 自带 MSBuildSDKsPath 也找不到,则回退到 <runtime>/lib/mono/msbuild/15.0/bin/Sdks。
  • 编译器仍然是 Roslyn csc/vbc,只是 targets 由 SDK 的 Microsoft.NET.Sdk 提供。
  • dotnet CLI 在 IDE 里只用于"运行/迁移"等场景,不参与构建。

七、编辑器内的编译器(容易混淆,需区分)

用途 实现 是否产出程序集
构建/生成 MSBuild Csc 任务 → Roslyn csc / VBCSCompiler 是
编辑器语义分析、补全、重构、错误波浪线 进程内 Roslyn Workspaces(Microsoft.CodeAnalysis 2.10 系列,见 main/packages) 否

所以"IDE 里没报错但编译失败"或反过来,往往是这两套编译环境(引用集合、语言版本、条件编译符号)不一致造成的,而不是"调用了另一个编译器"。

另外,MonoDevelop 对项目属性的读取(设计时求值)使用的是它自带的托管求值器

(DefaultMSBuildEngine,MSBuildEngineManager.cs:37-59;MSBuildEngineV12 相关代码已注释停用),

它不启动编译器;真正的编译只发生在构建阶段。


八、平台差异:Windows 上的 .NET Framework 项目

MsNetTargetRuntime 会把 MSBuild 目录解析到 Visual Studio 的 MSBuild\15.0\bin,

因此编译器来自 VS 自带的 Roslyn;链接方式与 Mono 完全一致(同样是 Csc 任务)。

本工作区是 Linux,实际走的是 Mono 分支。


九、本工作区的已知坑(与"编译器从哪加载"直接相关)

  1. $(RoslynTargetsPath) 为空导致 MSB4062

    症状:未能从程序集 /Microsoft.Build.Tasks.CodeAnalysis.dll 加载任务 Csc。

    原因:构建器工具集配置里的 $(MSBuildToolsPath)\Roslyn 未被展开,而 Microsoft.Common.tasks

    中 UsingTask 的注册早于全局属性生效。

    规避:在 ~/Projects/Directory.Build.targets 中用 $(RoslynTargetsPath) 重新声明 Csc/Vbc。

  2. GAC 引用解析不到导致 MSB3245

    症状:未能找到程序集"gtk-sharp"(命令行 msbuild 同项目却正常)。

    规避:同样在 Directory.Build.targets 里把 main/build/bin 追加进 AssemblySearchPaths。

  3. Gtk# 原生 glue 缺失导致运行时 DllNotFoundException: gtksharpglue-2

    这是运行期问题(非编译器问题),需在各 *-sharp.dll.config 中为 glue 添加 dllmap

    指向 mono-install/lib/lib*sharpglue-2.so;build_ide 已内置该补写。


十、如何自行验证

bash 复制代码
# 1) 用与 IDE 相同的构建栈构建(会打印 csc.exe 命令行)
cd <解决方案目录>
mono /media/deepseek2008/data/monodev/monodevelop/main/build/bin/mdtool.exe build xxx.sln

# 2) 查看编译器版本
mono /usr/lib/mono/4.5/csc.exe -version
mono /media/deepseek2008/data/monodev/mono-install/lib/mono/4.5/csc.exe -version

# 3) 观察构建器与编译服务器进程
ls ~/.cache/MonoDevelop/7.0/MSBuild/            # 每个 <pid>_<runtimeId> 是一套构建器+MSBuild+Roslyn
ps -ef | grep -E "MSBuildBuilder|VBCSCompiler"  # 构建器 / Roslyn 共享编译服务器

# 4) 在构建日志里确认 RoslynTargetsPath(可临时加诊断 Target)
#    <Message Importance="high" Text="ROSLYN=$(RoslynTargetsPath)" />

十一、代码位置索引

内容 位置
MSBuild 工具版本常量 15.0 monodevelop/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProjectService.cs:60
Mono 的 MSBuild 目录解析 monodevelop/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/MonoTargetRuntime.cs:180-198
构建器定位与缓存复制 monodevelop/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/RemoteBuildEngineManager.cs:440-518
传给 MSBuild 的全局属性(含 RoslynTargetsPath) 同上 :404-434
项目求值引擎(不调用编译器) monodevelop/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildEngineManager.cs:37-59
Csc 任务的 UsingTask 声明 <mono>/lib/mono/msbuild/15.0/bin/Microsoft.Common.tasks:182
Csc 任务调用点(ToolPath/ToolExe/UseSharedCompilation) <mono>/lib/mono/msbuild/15.0/bin/Roslyn/Microsoft.CSharp.Core.targets:71,137-140
Roslyn targets 引入点 <mono>/lib/mono/msbuild/15.0/bin/Microsoft.CSharp.CurrentVersion.targets:320-326
共享编译默认值 <mono>/lib/mono/msbuild/15.0/bin/Roslyn/Microsoft.Managed.Core.targets:48-49

十二、一句话总结

MonoDevelop 只把项目交给"目标运行时的 MSBuild 构建器",由 MSBuild 的 Csc/Vbc 任务

加载 $(RoslynTargetsPath) 下的 Roslyn(csc/vbc,或共享编译服务器 VBCSCompiler)完成编译;

mcs 已是历史遗留、默认不参与;编辑器里的 IntelliSense 使用同一家 Roslyn 但走进程内库,

不产出程序集。

相关推荐
will_net3 小时前
apollo 站点部署&C#接入
开发语言·c#·apollo
吾与谁归in10 小时前
C#脚本工具,支持智能提示自动补全(WPF + Roslyn + AvalonEdit)
开发语言·c#·wpf
昂好的day10 小时前
WPF 从零入门(上):WPF 是什么、XAML 语法、布局与样式全解
开发语言·c#·wpf
吃一碗粉只付一碗钱1 天前
10-令牌里的用户主键不要明文
服务器·数据库·sql·sqlserver·c#
胖纸不争1 天前
EnergyStarZ:让 Windows 后台进程给前台让路的能效管理器(v1.2 发布)
windows·性能优化·c#·win32·ef core
曹牧1 天前
C#:The operation has timed out
开发语言·c#
He BianGu1 天前
【WPF-Control教程】IOC 依赖注入
c#·wpf·wpf-control
He BianGu1 天前
【WPF-Control教程】H.Extensions.Behvaiors 开发文档
c#·wpf·wpf-control