Browse Source

Support Roslyn 1.x and 2.x compilers in tests on non-Windows

The Roslyn 1.3.2 and 2.10.0 configurations were excluded from the
compiler matrix on non-Windows platforms because Microsoft.Net.Compilers
only ships .NET Framework executables. Both can be enabled:

- Roslyn 2.10.0 has a dotnet-hosted sibling package,
  Microsoft.NETCore.Compilers, whose tools/bincore/csc.dll runs on the
  installed runtime with --roll-forward LatestMajor (its runtimeconfig
  pins the out-of-support .NET Core 2.0). Fetched on non-Windows into
  the version's tools/bincore directory; GetCSharpCompiler probes for a
  direct csc.dll next to the installed path in addition to the
  bincore/ subfolder layout of the newer toolset packages.
- Roslyn 1.3.2 has no .NET build; when a mono executable is found on
  the PATH, it is kept in the matrix and WrapCompiler hosts the .exe
  compilers through mono. Because the native DiaSymReader needed for
  Windows PDBs is unavailable there, GeneratePdb requests portable
  PDBs on non-Windows (Roslyn 2.x+ falls back on its own, 1.x needs
  the explicit -debug:portable).

The mcs configurations stay excluded: the bundled mcs 2.6.4 needs the
Reflection.Emit COMPILER_ACCESS mode that current Mono runtimes no
longer implement. Old-compiler configurations also stay excluded from
correctness-style fixtures: their output targets .NET Framework or
.NET Core 2.2, which the runners cannot execute here.
Microsoft.NETCore.Compilers-2.10.0.nupkg should be added to
ILSpy-tests/nuget to keep the fetch offline-capable.

Assisted-by: Claude:claude-fable-5:Claude Code
pull/3925/head
Siegfried Pammer 2 months ago committed by Siegfried Pammer
parent
commit
6ab0a48b0f
  1. 23
      ICSharpCode.Decompiler.Tests/Helpers/RoslynToolset.cs
  2. 95
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

23
ICSharpCode.Decompiler.Tests/Helpers/RoslynToolset.cs

@ -149,14 +149,33 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
// In the .NET ("netcore") build of the compiler toolset the executables live in a // In the .NET ("netcore") build of the compiler toolset the executables live in a
// "bincore" subfolder of the tasks directory, as .dlls launched through the dotnet host. // "bincore" subfolder of the tasks directory, as .dlls launched through the dotnet host.
// The old Microsoft.Net.Compilers packages (Roslyn 1.x/2.x) have no .NET build; they
// only ship .NET Framework executables, which non-Windows platforms host with Mono.
public string GetCSharpCompiler(string version) public string GetCSharpCompiler(string version)
{ {
return GetCompiler(OperatingSystem.IsWindows() ? "csc.exe" : "bincore/csc.dll", version); return GetHostedCompiler(version, "csc");
} }
public string GetVBCompiler(string version) public string GetVBCompiler(string version)
{ {
return GetCompiler(OperatingSystem.IsWindows() ? "vbc.exe" : "bincore/vbc.dll", version); return GetHostedCompiler(version, "vbc");
}
string GetHostedCompiler(string version, string name)
{
if (!OperatingSystem.IsWindows())
{
// The installed path may point directly at a dotnet-hosted compiler directory
// (e.g. Microsoft.NETCore.Compilers' tools/bincore) or at a tasks directory
// with a bincore subfolder (Microsoft.Net.Compilers.Toolset).
string dll = GetCompiler($"{name}.dll", version);
if (File.Exists(dll))
return dll;
dll = GetCompiler($"bincore/{name}.dll", version);
if (File.Exists(dll))
return dll;
}
return GetCompiler($"{name}.exe", version);
} }
string GetCompiler(string compiler, string version) string GetCompiler(string compiler, string version)

95
ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

@ -144,7 +144,17 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
internal static async Task Initialize() internal static async Task Initialize()
{ {
await roslynToolset.Fetch("1.3.2", "Microsoft.Net.Compilers", "tools").ConfigureAwait(false); await roslynToolset.Fetch("1.3.2", "Microsoft.Net.Compilers", "tools").ConfigureAwait(false);
await roslynToolset.Fetch("2.10.0", "Microsoft.Net.Compilers", "tools").ConfigureAwait(false); if (OperatingSystem.IsWindows())
{
await roslynToolset.Fetch("2.10.0", "Microsoft.Net.Compilers", "tools").ConfigureAwait(false);
}
else
{
// Microsoft.Net.Compilers only ships .NET Framework executables. The sibling
// Microsoft.NETCore.Compilers package contains the dotnet-hosted build of the
// same compiler version (tools/bincore/csc.dll), usable on any platform.
await roslynToolset.Fetch("2.10.0", "Microsoft.NETCore.Compilers", "tools/bincore").ConfigureAwait(false);
}
// On non-Windows hosts the net472 compiler binaries cannot be executed; use the // On non-Windows hosts the net472 compiler binaries cannot be executed; use the
// .NET build of each toolset instead. Its tasks folder is named "netcoreapp3.1" // .NET build of each toolset instead. Its tasks folder is named "netcoreapp3.1"
// up to Roslyn 3.x and "netcore" from Roslyn 4.x on. // up to Roslyn 3.x and "netcore" from Roslyn 4.x on.
@ -175,34 +185,73 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
} }
} }
/// <summary>
/// True when a Mono runtime is on the PATH. Mono hosts the .NET Framework builds of
/// compilers that have no .NET build (Roslyn 1.x csc.exe from Microsoft.Net.Compilers);
/// Roslyn 2.x+ configurations use dotnet-hosted builds and do not depend on Mono.
/// </summary>
public static readonly bool MonoRuntimeAvailable =
!OperatingSystem.IsWindows()
&& (Environment.GetEnvironmentVariable("PATH") ?? "")
.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries)
.Any(dir => File.Exists(Path.Combine(dir, "mono")));
/// <summary> /// <summary>
/// Reduces a compiler-configuration matrix to the entries usable on the current platform. /// Reduces a compiler-configuration matrix to the entries usable on the current platform.
/// On Windows every configuration is supported. On other platforms, configurations that /// On Windows every configuration is supported. On other platforms, configurations that
/// depend on Windows-only tools or runtimes are removed: the legacy (pre-Roslyn) csc/vbc, /// depend on Windows-only tools or runtimes are removed: the legacy (pre-Roslyn) csc/vbc
/// Roslyn 1.x/2.x (their packages only ship .NET Framework binaries), mcs, and Force32Bit /// and Force32Bit (requires a 32-bit runtime) always; Roslyn 1.x/2.x (their packages only
/// (requires a 32-bit runtime). When <paramref name="executesCompiledOutput"/> is set /// ship .NET Framework binaries) and mcs unless a Mono runtime is available to host them.
/// (correctness-style fixtures), configurations targeting .NET Framework 4.0 are removed /// When <paramref name="executesCompiledOutput"/> is set (correctness-style fixtures),
/// as well, because their output executables only run on Windows. /// configurations targeting .NET Framework 4.0 are removed as well, because their output
/// executables only run on Windows, and the Mono-hosted compilers are not included either
/// (their output would likewise need a Mono host to execute).
/// </summary> /// </summary>
public static CompilerOptions[] SupportedOnCurrentPlatform(CompilerOptions[] configurations, bool executesCompiledOutput = false) public static CompilerOptions[] SupportedOnCurrentPlatform(CompilerOptions[] configurations, bool executesCompiledOutput = false)
{ {
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
return configurations; return configurations;
const CompilerOptions dotnetHostedCompilers = CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.UseRoslyn4_14_0 | CompilerOptions.UseRoslynLatest; const CompilerOptions dotnetHostedCompilers = CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.UseRoslyn4_14_0 | CompilerOptions.UseRoslynLatest;
return configurations.Where(c => (c & dotnetHostedCompilers) != 0 CompilerOptions supportedCompilers = dotnetHostedCompilers;
if (!executesCompiledOutput)
{
// The dotnet-hosted Roslyn 2.x build (Microsoft.NETCore.Compilers) can compile
// on any platform, but its output targets .NET Core 2.2 or .NET Framework,
// which the correctness runners cannot execute here.
supportedCompilers |= CompilerOptions.UseRoslyn2_10_0;
// Roslyn 1.3.2 has only a .NET Framework build and needs Mono as its host.
// The configuration stays in the matrix even without Mono so that a missing
// runtime shows up as skipped tests (see WrapCompiler) instead of a silently
// smaller matrix. mcs 2.6.4 stays excluded: it needs the Reflection.Emit
// COMPILER_ACCESS mode that current Mono runtimes no longer implement.
supportedCompilers |= CompilerOptions.UseRoslyn1_3_2;
}
return configurations.Where(c => (c & supportedCompilers) != 0
&& !c.HasFlag(CompilerOptions.Force32Bit) && !c.HasFlag(CompilerOptions.Force32Bit)
&& !(executesCompiledOutput && c.HasFlag(CompilerOptions.TargetNet40))).ToArray(); && !(executesCompiledOutput && c.HasFlag(CompilerOptions.TargetNet40))).ToArray();
} }
/// <summary> /// <summary>
/// Wraps an external compiler invocation. The .NET Framework builds of the Roslyn /// Wraps an external compiler invocation. The .NET Framework builds of the Roslyn
/// compilers (csc.exe/vbc.exe) are directly executable; the .NET builds ship as a /// compilers (csc.exe/vbc.exe) are directly executable on Windows and are hosted by
/// .dll that must be launched through the 'dotnet' host. /// Mono elsewhere; the .NET builds ship as a .dll that must be launched through the
/// 'dotnet' host.
/// </summary> /// </summary>
static Command WrapCompiler(string compilerPath, string arguments) static Command WrapCompiler(string compilerPath, string arguments)
{ {
// Old compiler builds pin an out-of-support runtime in their runtimeconfig
// (e.g. Roslyn 2.x pins Microsoft.NETCore.App 2.0), so allow the host to
// roll forward across major versions to whatever runtime is installed.
if (compilerPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) if (compilerPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
return Cli.Wrap("dotnet").WithArguments($"\"{compilerPath}\" {arguments}"); return Cli.Wrap("dotnet").WithArguments($"--roll-forward LatestMajor \"{compilerPath}\" {arguments}");
if (!OperatingSystem.IsWindows() && compilerPath.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
{
if (!MonoRuntimeAvailable)
{
Assert.Ignore($"{Path.GetFileName(compilerPath)} requires a Mono runtime on the PATH to run on this platform.");
}
return Cli.Wrap("mono").WithArguments($"\"{compilerPath}\" {arguments}");
}
return Cli.Wrap(compilerPath).WithArguments(arguments); return Cli.Wrap(compilerPath).WithArguments(arguments);
} }
@ -695,7 +744,17 @@ namespace System.Runtime.CompilerServices
if (flags.HasFlag(CompilerOptions.GeneratePdb)) if (flags.HasFlag(CompilerOptions.GeneratePdb))
{ {
otherOptions += "-debug:full "; // The Windows PDB writer needs the native DiaSymReader, so only portable
// PDBs work on other platforms. Roslyn 2.x+ falls back to portable on its
// own; Roslyn 1.x needs the explicit request.
if (!OperatingSystem.IsWindows() && (flags & CompilerOptions.UseRoslynMask) != 0)
{
otherOptions += "-debug:portable ";
}
else
{
otherOptions += "-debug:full ";
}
} }
else else
{ {
@ -764,9 +823,14 @@ namespace System.Runtime.CompilerServices
Assert.Ignore($"Compilation with mcs ignored: test directory '{testBasePath}' needs to be checked out separately." + Environment.NewLine + Assert.Ignore($"Compilation with mcs ignored: test directory '{testBasePath}' needs to be checked out separately." + Environment.NewLine +
$"git clone https://github.com/icsharpcode/ILSpy-tests \"{testBasePath}\""); $"git clone https://github.com/icsharpcode/ILSpy-tests \"{testBasePath}\"");
} }
string mcsPath = (flags & CompilerOptions.UseMcsMask) switch { // On Windows the submodule's bat launchers run the bundled Windows-native Mono;
CompilerOptions.UseMcs5_23 => Path.Combine(testBasePath, @"mcs\5.23\bin\mcs.bat"), // elsewhere the bundled compilers (managed .NET Framework assemblies) are hosted
_ => Path.Combine(testBasePath, @"mcs\2.6.4\bin\gmcs.bat") // by the system Mono runtime via WrapCompiler.
string mcsPath = (flags & CompilerOptions.UseMcsMask, OperatingSystem.IsWindows()) switch {
(CompilerOptions.UseMcs5_23, true) => Path.Combine(testBasePath, @"mcs\5.23\bin\mcs.bat"),
(CompilerOptions.UseMcs5_23, false) => Path.Combine(testBasePath, "mcs", "5.23", "lib", "mono", "4.5", "mcs.exe"),
(_, true) => Path.Combine(testBasePath, @"mcs\2.6.4\bin\gmcs.bat"),
(_, false) => Path.Combine(testBasePath, "mcs", "2.6.4", "lib", "mono", "2.0", "gmcs.exe")
}; };
string otherOptions = " -unsafe -o" + (flags.HasFlag(CompilerOptions.Optimize) ? "+ " : "- "); string otherOptions = " -unsafe -o" + (flags.HasFlag(CompilerOptions.Optimize) ? "+ " : "- ");
@ -797,8 +861,7 @@ namespace System.Runtime.CompilerServices
otherOptions += " \"-d:" + string.Join(";", preprocessorSymbols) + "\" "; otherOptions += " \"-d:" + string.Join(";", preprocessorSymbols) + "\" ";
} }
var command = Cli.Wrap(mcsPath) var command = WrapCompiler(mcsPath, $"{otherOptions}-out:\"{Path.GetFullPath(results.PathToAssembly)}\" {string.Join(" ", sourceFileNames.Select(fn => '"' + Path.GetFullPath(fn) + '"'))}")
.WithArguments($"{otherOptions}-out:\"{Path.GetFullPath(results.PathToAssembly)}\" {string.Join(" ", sourceFileNames.Select(fn => '"' + Path.GetFullPath(fn) + '"'))}")
.WithValidation(CommandResultValidation.None); .WithValidation(CommandResultValidation.None);
//Console.WriteLine($"\"{command.TargetFilePath}\" {command.Arguments}"); //Console.WriteLine($"\"{command.TargetFilePath}\" {command.Arguments}");

Loading…
Cancel
Save