Skip to content

Commit 8beca08

Browse files
committed
Fix cross-os symbol generation (dotnet#20481)
* Fix cross-os symbol generation - Use the target os to determine the symbol type, not the sdk os - Add tests for cross os/cross arch crossgen compilation * Fix tests to specify composite accurately to TestProjectPublishing_Internal, as the new test logic demands correctness
1 parent cdca9ab commit 8beca08

File tree

4 files changed

+163
-23
lines changed

4 files changed

+163
-23
lines changed

src/Tasks/Microsoft.NET.Build.Tasks/PrepareForReadyToRunCompilation.cs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,30 @@ public class PrepareForReadyToRunCompilation : TaskBase
6666
private List<ITaskItem> _r2rCompositeReferences = new List<ITaskItem>();
6767
private List<ITaskItem> _r2rCompositeInput = new List<ITaskItem>();
6868

69+
private bool IsTargetWindows
70+
{
71+
get
72+
{
73+
// Crossgen2 V6 and above always has TargetOS metadata available
74+
if (ReadyToRunUseCrossgen2 && !string.IsNullOrEmpty(Crossgen2Tool.GetMetadata(MetadataKeys.TargetOS)))
75+
return Crossgen2Tool.GetMetadata(MetadataKeys.TargetOS) == "windows";
76+
else
77+
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
78+
}
79+
}
80+
81+
private bool IsTargetLinux
82+
{
83+
get
84+
{
85+
// Crossgen2 V6 and above always has TargetOS metadata available
86+
if (ReadyToRunUseCrossgen2 && !string.IsNullOrEmpty(Crossgen2Tool.GetMetadata(MetadataKeys.TargetOS)))
87+
return Crossgen2Tool.GetMetadata(MetadataKeys.TargetOS) == "linux";
88+
else
89+
return RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
90+
}
91+
}
92+
6993
protected override void ExecuteCore()
7094
{
7195
if (ReadyToRunUseCrossgen2)
@@ -140,13 +164,13 @@ private void ProcessInputFileList(
140164

141165
if (EmitSymbols)
142166
{
143-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && hasValidDiaSymReaderLib)
167+
if (IsTargetWindows && hasValidDiaSymReaderLib)
144168
{
145169
outputPDBImage = Path.ChangeExtension(outputR2RImage, "ni.pdb");
146170
outputPDBImageRelativePath = Path.ChangeExtension(outputR2RImageRelativePath, "ni.pdb");
147171
crossgen1CreatePDBCommand = $"/CreatePDB \"{Path.GetDirectoryName(outputPDBImage)}\"";
148172
}
149-
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
173+
else if (IsTargetLinux)
150174
{
151175
string perfmapExtension;
152176
if (ReadyToRunUseCrossgen2 && !_crossgen2IsVersion5 && _perfmapFormatVersion >= 1)
@@ -246,12 +270,12 @@ private void ProcessInputFileList(
246270
{
247271
string compositePDBImage = null;
248272
string compositePDBRelativePath = null;
249-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && hasValidDiaSymReaderLib)
273+
if (IsTargetWindows && hasValidDiaSymReaderLib)
250274
{
251275
compositePDBImage = Path.ChangeExtension(compositeR2RImage, ".ni.pdb");
252276
compositePDBRelativePath = Path.ChangeExtension(compositeR2RImageRelativePath, ".ni.pdb");
253277
}
254-
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
278+
else if (IsTargetLinux)
255279
{
256280
string perfmapExtension = (_perfmapFormatVersion >= 1 ? ".ni.r2rmap" : ".ni.{composite}.map");
257281
compositePDBImage = Path.ChangeExtension(compositeR2RImage, perfmapExtension);

src/Tasks/Microsoft.NET.Build.Tasks/RunReadyToRunCompiler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ private string GenerateCrossgen2ResponseFile()
318318
// 5.0 Crossgen2 doesn't support PDB generation.
319319
if (!Crossgen2IsVersion5 && _emitSymbols)
320320
{
321-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
321+
if (Crossgen2Tool.GetMetadata(MetadataKeys.TargetOS) == "windows")
322322
{
323323
result.AppendLine("--pdb");
324324
result.AppendLine($"--pdb-path:{Path.GetDirectoryName(_outputPDBImage)}");

src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishASingleFileApp.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ public void It_excludes_ni_pdbs_from_single_file()
451451

452452
var intermediateDirectory = publishCommand.GetIntermediateDirectory(targetFramework, runtimeIdentifier: RuntimeInformation.RuntimeIdentifier);
453453
var mainProjectDll = Path.Combine(intermediateDirectory.FullName, $"{TestProjectName}.dll");
454-
var niPdbFile = GivenThatWeWantToPublishReadyToRun.GetPDBFileName(mainProjectDll, framework);
454+
var niPdbFile = GivenThatWeWantToPublishReadyToRun.GetPDBFileName(mainProjectDll, framework, RuntimeInformation.RuntimeIdentifier);
455455

456456
string[] expectedFiles = { SingleFile, PdbFile, niPdbFile };
457457
GetPublishDirectory(publishCommand)

src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishReadyToRun.cs

Lines changed: 133 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.Linq;
45
using System.Reflection.Metadata;
56
using System.Reflection.PortableExecutable;
67
using System.Runtime.CompilerServices;
@@ -86,8 +87,8 @@ public void It_creates_readytorun_images_for_all_assemblies_except_excluded_ones
8687
NuGetFramework framework = NuGetFramework.Parse(targetFramework);
8788

8889
publishDirectory.Should().NotHaveFiles(new[] {
89-
GetPDBFileName(mainProjectDll, framework),
90-
GetPDBFileName(classLibDll, framework),
90+
GetPDBFileName(mainProjectDll, framework, testProject.RuntimeIdentifier),
91+
GetPDBFileName(classLibDll, framework, testProject.RuntimeIdentifier),
9192
});
9293
}
9394

@@ -112,7 +113,7 @@ public void It_creates_readytorun_symbols_when_switch_is_used(string targetFrame
112113
[InlineData("net6.0")]
113114
public void It_supports_framework_dependent_publishing(string targetFramework)
114115
{
115-
TestProjectPublishing_Internal("FrameworkDependent", targetFramework, isSelfContained: false, emitNativeSymbols:true, identifier: targetFramework);
116+
TestProjectPublishing_Internal("FrameworkDependent", targetFramework, isSelfContained: false, composite: false, emitNativeSymbols:true, identifier: targetFramework);
116117
}
117118

118119
[Theory]
@@ -199,7 +200,7 @@ public void It_warns_when_targetting_netcoreapp_2_x_readytorun()
199200
[InlineData("net6.0")]
200201
public void It_can_publish_readytorun_for_library_projects(string targetFramework)
201202
{
202-
TestProjectPublishing_Internal("LibraryProject1", targetFramework, isSelfContained: false, makeExeProject: false, identifier: targetFramework);
203+
TestProjectPublishing_Internal("LibraryProject1", targetFramework, isSelfContained: false, composite: false, makeExeProject: false, identifier: targetFramework);
203204
}
204205

205206
[RequiresMSBuildVersionTheory("17.0.0.32901")]
@@ -208,7 +209,7 @@ public void It_can_publish_readytorun_for_library_projects(string targetFramewor
208209
[InlineData("net6.0")]
209210
public void It_can_publish_readytorun_for_selfcontained_library_projects(string targetFramework)
210211
{
211-
TestProjectPublishing_Internal("LibraryProject2", targetFramework, isSelfContained:true, makeExeProject: false, identifier: targetFramework);
212+
TestProjectPublishing_Internal("LibraryProject2", targetFramework, isSelfContained:true, composite: true, makeExeProject: false, identifier: targetFramework);
212213
}
213214

214215
[RequiresMSBuildVersionTheory("17.0.0.32901")]
@@ -264,6 +265,103 @@ public void It_supports_libraries_when_using_crossgen2(string targetFramework)
264265
publishCommand.Execute().Should().Pass();
265266
}
266267

268+
[RequiresMSBuildVersionTheory("17.0.0.32901")]
269+
[InlineData("net6.0", "linux-x64", "windows,linux,osx", "X64,Arm64", "_", "_")]
270+
[InlineData("net6.0", "linux-x64", "windows,linux,osx", "X64,Arm64", "composite", "selfcontained")] // Composite in .NET 6.0 is only supported for self-contained builds
271+
// In .NET 6.0 building targetting Windows on linux or osx doesn't support emitting native symbols.
272+
[InlineData("net6.0", "win-x64", "windows", "X64,Arm64", "composite", "selfcontained")] // Composite in .NET 6.0 is only supported for self-contained builds
273+
[InlineData("net6.0", "osx-arm64", "windows,linux,osx", "X64,Arm64", "_", "_")]
274+
// In .NET 6.0 building targetting Windows on linux or osx doesn't support emitting native symbols.
275+
[InlineData("net6.0", "win-x86", "windows", "X86,X64,Arm64,Arm", "_", "_")]
276+
public void It_supports_crossos_arch_compilation(string targetFramework, string runtimeIdentifier, string sdkSupportedOs, string sdkSupportedArch, string composite, string selfcontained)
277+
{
278+
var projectName = $"CrossArchOs{targetFramework}{runtimeIdentifier.Replace("-",".")}{composite}{selfcontained}";
279+
string sdkOs = "NOTHING";
280+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
281+
{
282+
sdkOs = "linux";
283+
}
284+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
285+
{
286+
sdkOs = "windows";
287+
}
288+
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
289+
{
290+
sdkOs = "osx";
291+
}
292+
293+
Assert.NotEqual("NOTHING", sdkOs); // We should know which OS we are running on
294+
Log.WriteLine($"sdkOs = {sdkOs}");
295+
if (!sdkSupportedOs.Contains(sdkOs))
296+
{
297+
Log.WriteLine("Running test on OS that doesn't support this cross platform build");
298+
return;
299+
}
300+
301+
string sdkArch = RuntimeInformation.ProcessArchitecture.ToString();
302+
Log.WriteLine($"sdkArch = {sdkArch}");
303+
Assert.Contains(sdkArch, new string[]{"Arm", "Arm64", "X64", "X86"}); // Assert that the Architecture in use is a known architecture
304+
if (!sdkSupportedArch.Split(',').Contains(sdkArch))
305+
{
306+
Log.WriteLine("Running test on processor architecture that doesn't support this cross platform build");
307+
return;
308+
}
309+
310+
TestProjectPublishing_Internal(projectName, targetFramework, isSelfContained: selfcontained == "selfcontained", emitNativeSymbols: true, useCrossgen2: true, composite: composite == "composite", identifier: targetFramework, runtimeIdentifier: runtimeIdentifier);
311+
}
312+
313+
private enum TargetOSEnum
314+
{
315+
Windows,
316+
Linux,
317+
OsX
318+
}
319+
320+
private static TargetOSEnum GetTargetOS(string runtimeIdentifier)
321+
{
322+
if (runtimeIdentifier.Contains("osx"))
323+
{
324+
return TargetOSEnum.OsX;
325+
}
326+
else if (runtimeIdentifier.Contains("win"))
327+
{
328+
return TargetOSEnum.Windows;
329+
}
330+
else if (runtimeIdentifier.Contains("linux") ||
331+
runtimeIdentifier.Contains("ubuntu") ||
332+
runtimeIdentifier.Contains("alpine") ||
333+
runtimeIdentifier.Contains("android") ||
334+
runtimeIdentifier.Contains("centos") ||
335+
runtimeIdentifier.Contains("debian") ||
336+
runtimeIdentifier.Contains("fedora") ||
337+
runtimeIdentifier.Contains("gentoo") ||
338+
runtimeIdentifier.Contains("suse") ||
339+
runtimeIdentifier.Contains("rhel") ||
340+
runtimeIdentifier.Contains("sles") ||
341+
runtimeIdentifier.Contains("tizen"))
342+
{
343+
return TargetOSEnum.Linux;
344+
}
345+
346+
Assert.True(false, $"{runtimeIdentifier} could not be converted into a known OS type. Adjust the if statement above until this does not happen");
347+
return TargetOSEnum.Windows;
348+
}
349+
350+
private static bool IsTargetOsOsX(string runtimeIdentifier)
351+
{
352+
return GetTargetOS(runtimeIdentifier) == TargetOSEnum.OsX;
353+
}
354+
355+
private static bool IsTargetOsWindows(string runtimeIdentifier)
356+
{
357+
return GetTargetOS(runtimeIdentifier) == TargetOSEnum.Windows;
358+
}
359+
360+
private static bool IsTargetOsLinux(string runtimeIdentifier)
361+
{
362+
return GetTargetOS(runtimeIdentifier) == TargetOSEnum.Linux;
363+
}
364+
267365
private void TestProjectPublishing_Internal(string projectName,
268366
string targetFramework,
269367
bool makeExeProject = true,
@@ -272,13 +370,15 @@ private void TestProjectPublishing_Internal(string projectName,
272370
bool useCrossgen2 = false,
273371
bool composite = true,
274372
[CallerMemberName] string callingMethod = "",
275-
string identifier = null)
373+
string identifier = null,
374+
string runtimeIdentifier = null)
276375
{
277376
var testProject = CreateTestProjectForR2RTesting(
278377
targetFramework,
279378
projectName,
280379
"ClassLib",
281-
isExeProject: makeExeProject);
380+
isExeProject: makeExeProject,
381+
runtimeIdentifier: runtimeIdentifier);
282382

283383
testProject.AdditionalProperties["PublishReadyToRun"] = "True";
284384
testProject.AdditionalProperties["PublishReadyToRunEmitSymbols"] = emitNativeSymbols ? "True" : "False";
@@ -307,18 +407,34 @@ private void TestProjectPublishing_Internal(string projectName,
307407
else
308408
publishDirectory.Should().NotHaveFile("System.Private.CoreLib.dll");
309409

310-
if (emitNativeSymbols && !RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
410+
if (emitNativeSymbols && !IsTargetOsOsX(testProject.RuntimeIdentifier))
311411
{
312412
NuGetFramework framework = NuGetFramework.Parse(targetFramework);
413+
Log.WriteLine("Checking for symbol files");
414+
IEnumerable<string> pdbFiles;
313415

314-
publishDirectory.Should().HaveFiles(new[] {
315-
GetPDBFileName(mainProjectDll, framework),
316-
GetPDBFileName(classLibDll, framework),
317-
});
416+
if (composite)
417+
{
418+
pdbFiles = new[] { GetPDBFileName(Path.ChangeExtension(mainProjectDll, "r2r.dll"), framework, testProject.RuntimeIdentifier) };
419+
}
420+
else
421+
{
422+
pdbFiles = new[] {
423+
GetPDBFileName(mainProjectDll, framework, testProject.RuntimeIdentifier),
424+
GetPDBFileName(classLibDll, framework, testProject.RuntimeIdentifier),
425+
};
426+
}
427+
428+
foreach (string s in pdbFiles)
429+
{
430+
Log.WriteLine($"{publishDirectory.FullName} {s}");
431+
}
432+
433+
publishDirectory.Should().HaveFiles(pdbFiles);
318434
}
319435
}
320436

321-
private TestProject CreateTestProjectForR2RTesting(string targetFramework, string mainProjectName, string referenceProjectName, bool isExeProject = true)
437+
private TestProject CreateTestProjectForR2RTesting(string targetFramework, string mainProjectName, string referenceProjectName, bool isExeProject = true, string runtimeIdentifier = null)
322438
{
323439
var referenceProject = new TestProject()
324440
{
@@ -340,7 +456,7 @@ public string Func()
340456
Name = mainProjectName,
341457
TargetFrameworks = targetFramework,
342458
IsExe = isExeProject,
343-
RuntimeIdentifier = EnvironmentInfo.GetCompatibleRid(targetFramework),
459+
RuntimeIdentifier = runtimeIdentifier ?? EnvironmentInfo.GetCompatibleRid(targetFramework),
344460
ReferencedProjects = { referenceProject },
345461
};
346462
testProject.SourceFiles[$"{mainProjectName}.cs"] = @"
@@ -356,14 +472,14 @@ public static void Main()
356472
return testProject;
357473
}
358474

359-
public static string GetPDBFileName(string assemblyFile, NuGetFramework framework)
475+
public static string GetPDBFileName(string assemblyFile, NuGetFramework framework, string runtimeIdentifier)
360476
{
361-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
477+
if (IsTargetOsWindows(runtimeIdentifier))
362478
{
363479
return Path.GetFileName(Path.ChangeExtension(assemblyFile, "ni.pdb"));
364480
}
365481

366-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
482+
if (IsTargetOsLinux(runtimeIdentifier))
367483
{
368484
if (framework.Version.Major >= 6)
369485
{

0 commit comments

Comments
 (0)