Skip to content

Commit f4667e4

Browse files
author
Livar
authored
Merge pull request #1387 from ericstj/ns15-supportLibs
Add netstandard support facades when ns1.5+ libraries are referenced
2 parents cfbd651 + c602e1a commit f4667e4

File tree

5 files changed

+123
-9
lines changed

5 files changed

+123
-9
lines changed

src/Tasks/Microsoft.NET.Build.Extensions.Tasks.UnitTests/GivenAGetDependsOnNETStandardTask.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ public void CanCheckThisAssembly()
2828
};
2929
task.Execute().Should().BeTrue();
3030

31-
// this test happens to compile against System.Runtime, update if the test TFM changes to netstandard2.x
32-
task.DependsOnNETStandard.Should().BeFalse();
31+
// this test compiles against a sufficiently high System.Runtime
32+
task.DependsOnNETStandard.Should().BeTrue();
3333
}
3434

3535
[Fact]
@@ -51,8 +51,8 @@ public void CanCheckThisAssemblyByHintPath()
5151
};
5252
task.Execute().Should().BeTrue();
5353

54-
// this test happens to compile against System.Runtime, update if the test TFM changes to netstandard2.x
55-
task.DependsOnNETStandard.Should().BeFalse();
54+
// this test compiles against a sufficiently high System.Runtime
55+
task.DependsOnNETStandard.Should().BeTrue();
5656
}
5757

5858

src/Tasks/Microsoft.NET.Build.Extensions.Tasks/GetDependsOnNETStandard.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ namespace Microsoft.NET.Build.Tasks
1414
public partial class GetDependsOnNETStandard : TaskBase
1515
{
1616
private const string NetStandardAssemblyName = "netstandard";
17+
18+
// System.Runtime from netstandard1.5
19+
// We also treat this as depending on netstandard so that we can provide netstandard1.5 and netstandard1.6 compatible
20+
// facades since net461 was previously only compatible with netstandard1.4 and thus packages only provided netstandard1.4
21+
// compatible facades.
22+
private const string SystemRuntimeAssemblyName = "System.Runtime";
23+
private static readonly Version SystemRuntimeMinVersion = new Version(4, 1, 0, 0);
24+
1725
/// <summary>
1826
/// Set of reference items to analyze.
1927
/// </summary>

src/Tasks/Microsoft.NET.Build.Extensions.Tasks/GetDependsOnNETStandard.net46.cs

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,17 @@ internal static bool GetFileDependsOnNETStandard(string filePath)
4848
var asmRefEnum = IntPtr.Zero;
4949
var asmRefTokens = new UInt32[16];
5050
UInt32 fetched;
51+
52+
var assemblyMD = new ASSEMBLYMETADATA()
53+
{
54+
rpLocale = IntPtr.Zero,
55+
cchLocale = 0,
56+
rpProcessors = IntPtr.Zero,
57+
cProcessors = 0,
58+
rOses = IntPtr.Zero,
59+
cOses = 0
60+
};
61+
5162
// Ensure the enum handle is closed.
5263
try
5364
{
@@ -72,7 +83,7 @@ internal static bool GetFileDependsOnNETStandard(string filePath)
7283
null,
7384
0,
7485
out asmNameLength,
75-
IntPtr.Zero,
86+
ref assemblyMD,
7687
out hashDataPtr,
7788
out hashDataLength,
7889
out flags);
@@ -88,7 +99,7 @@ internal static bool GetFileDependsOnNETStandard(string filePath)
8899
assemblyNameBuffer,
89100
(uint)assemblyNameBuffer.Capacity,
90101
out asmNameLength,
91-
IntPtr.Zero,
102+
ref assemblyMD,
92103
out hashDataPtr,
93104
out hashDataLength,
94105
out flags);
@@ -99,6 +110,16 @@ internal static bool GetFileDependsOnNETStandard(string filePath)
99110
{
100111
return true;
101112
}
113+
114+
if (assemblyName.Equals(SystemRuntimeAssemblyName, StringComparison.Ordinal))
115+
{
116+
var assemblyVersion = new Version(assemblyMD.usMajorVersion, assemblyMD.usMinorVersion, assemblyMD.usBuildNumber, assemblyMD.usRevisionNumber);
117+
118+
if (assemblyVersion >= SystemRuntimeMinVersion)
119+
{
120+
return true;
121+
}
122+
}
102123
}
103124
} while (fetched > 0);
104125
}
@@ -145,7 +166,7 @@ internal interface IMetaDataDispenser
145166
internal interface IMetaDataAssemblyImport
146167
{
147168
void GetAssemblyProps(UInt32 mdAsm, out IntPtr pPublicKeyPtr, out UInt32 ucbPublicKeyPtr, out UInt32 uHashAlg, StringBuilder strName, UInt32 cchNameIn, out UInt32 cchNameRequired, IntPtr amdInfo, out UInt32 dwFlags);
148-
void GetAssemblyRefProps(UInt32 mdAsmRef, out IntPtr ppbPublicKeyOrToken, out UInt32 pcbPublicKeyOrToken, StringBuilder strName, UInt32 cchNameIn, out UInt32 pchNameOut, IntPtr amdInfo, out IntPtr ppbHashValue, out UInt32 pcbHashValue, out UInt32 pdwAssemblyRefFlags);
169+
void GetAssemblyRefProps(UInt32 mdAsmRef, out IntPtr ppbPublicKeyOrToken, out UInt32 pcbPublicKeyOrToken, StringBuilder strName, UInt32 cchNameIn, out UInt32 pchNameOut, ref ASSEMBLYMETADATA amdInfo, out IntPtr ppbHashValue, out UInt32 pcbHashValue, out UInt32 pdwAssemblyRefFlags);
149170
void GetFileProps([In] UInt32 mdFile, StringBuilder strName, UInt32 cchName, out UInt32 cchNameRequired, out IntPtr bHashData, out UInt32 cchHashBytes, out UInt32 dwFileFlags);
150171
void GetExportedTypeProps();
151172
void GetManifestResourceProps();
@@ -162,6 +183,39 @@ internal interface IMetaDataAssemblyImport
162183
void CloseEnum([In] IntPtr phEnum);
163184
void FindAssembliesByName();
164185
}
186+
187+
188+
/*
189+
From cor.h:
190+
typedef struct
191+
{
192+
USHORT usMajorVersion; // Major Version.
193+
USHORT usMinorVersion; // Minor Version.
194+
USHORT usBuildNumber; // Build Number.
195+
USHORT usRevisionNumber; // Revision Number.
196+
LPWSTR szLocale; // Locale.
197+
ULONG cbLocale; // [IN/OUT] Size of the buffer in wide chars/Actual size.
198+
DWORD *rProcessor; // Processor ID array.
199+
ULONG ulProcessor; // [IN/OUT] Size of the Processor ID array/Actual # of entries filled in.
200+
OSINFO *rOS; // OSINFO array.
201+
ULONG ulOS; // [IN/OUT]Size of the OSINFO array/Actual # of entries filled in.
202+
} ASSEMBLYMETADATA;
203+
*/
204+
[StructLayout(LayoutKind.Sequential)]
205+
internal struct ASSEMBLYMETADATA
206+
{
207+
public UInt16 usMajorVersion;
208+
public UInt16 usMinorVersion;
209+
public UInt16 usBuildNumber;
210+
public UInt16 usRevisionNumber;
211+
public IntPtr rpLocale;
212+
public UInt32 cchLocale;
213+
public IntPtr rpProcessors;
214+
public UInt32 cProcessors;
215+
public IntPtr rOses;
216+
public UInt32 cOses;
217+
}
218+
165219
#endregion
166220
}
167221
}

src/Tasks/Microsoft.NET.Build.Extensions.Tasks/GetDependsOnNETStandard.netstandard.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ internal static bool GetFileDependsOnNETStandard(string filePath)
2929
{
3030
return true;
3131
}
32+
33+
if (reader.StringComparer.Equals(reference.Name, SystemRuntimeAssemblyName) &&
34+
reference.Version >= SystemRuntimeMinVersion)
35+
{
36+
return true;
37+
}
3238
}
3339
}
3440
}

test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildADesktopExeWtihNetStandardLib.cs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ public void It_does_not_include_netstandard_when_inbox(bool isSdk)
294294
[WindowsOnlyTheory]
295295
[InlineData(true)]
296296
[InlineData(false)]
297-
public void It_does_not_include_netstandard_when_libary_targets_netstandard1(bool isSdk)
297+
public void It_does_not_include_netstandard_when_libary_targets_netstandard14(bool isSdk)
298298
{
299299
var testAsset = _testAssetsManager
300300
.CopyTestAsset(GetTemplateName(isSdk))
@@ -311,7 +311,7 @@ public void It_does_not_include_netstandard_when_libary_targets_netstandard1(boo
311311
var ns = project.Root.Name.Namespace;
312312
var propertyGroup = project.Root.Elements(ns + "PropertyGroup").First();
313313
var targetFrameworkProperty = propertyGroup.Element(ns + "TargetFramework");
314-
targetFrameworkProperty.Value = "netstandard1.6";
314+
targetFrameworkProperty.Value = "netstandard1.4";
315315
}
316316
});
317317

@@ -330,5 +330,51 @@ public void It_does_not_include_netstandard_when_libary_targets_netstandard1(boo
330330
outputDirectory.Should().NotHaveFile("netstandard.dll");
331331
}
332332

333+
334+
[WindowsOnlyTheory]
335+
[InlineData(true)]
336+
[InlineData(false)]
337+
public void It_includes_netstandard_when_libary_targets_netstandard15(bool isSdk)
338+
{
339+
var testAsset = _testAssetsManager
340+
.CopyTestAsset(GetTemplateName(isSdk))
341+
.WithSource()
342+
.WithProjectChanges((projectPath, project) =>
343+
{
344+
if (IsAppProject(projectPath))
345+
{
346+
AddReferenceToLibrary(project, ReferenceScenario.ProjectReference);
347+
}
348+
349+
if (IsLibraryProject(projectPath))
350+
{
351+
var ns = project.Root.Name.Namespace;
352+
var propertyGroup = project.Root.Elements(ns + "PropertyGroup").First();
353+
var targetFrameworkProperty = propertyGroup.Element(ns + "TargetFramework");
354+
targetFrameworkProperty.Value = "netstandard1.5";
355+
}
356+
});
357+
358+
testAsset.Restore(Log, relativePath: AppName);
359+
360+
var buildCommand = new BuildCommand(Log, Path.Combine(testAsset.TestRoot, AppName));
361+
buildCommand
362+
.Execute()
363+
.Should()
364+
.Pass();
365+
366+
var outputDirectory = isSdk ?
367+
buildCommand.GetOutputDirectory("net461") :
368+
buildCommand.GetNonSDKOutputDirectory();
369+
370+
// NET461 didn't originally support netstandard2.0, (nor netstandard1.5 or netstandard1.6)
371+
// Since support was added after we need to ensure we apply the shims for netstandard1.5 projects as well.
372+
373+
outputDirectory.Should().HaveFiles(new[] {
374+
"netstandard.dll",
375+
$"{AppName}.exe.config"
376+
});
377+
}
378+
333379
}
334380
}

0 commit comments

Comments
 (0)