Skip to content

Add netstandard support facades when ns1.5+ libraries are referenced #1387

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public void CanCheckThisAssembly()
};
task.Execute().Should().BeTrue();

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

[Fact]
Expand All @@ -51,8 +51,8 @@ public void CanCheckThisAssemblyByHintPath()
};
task.Execute().Should().BeTrue();

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


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ namespace Microsoft.NET.Build.Tasks
public partial class GetDependsOnNETStandard : TaskBase
{
private const string NetStandardAssemblyName = "netstandard";

// System.Runtime from netstandard1.5
// We also treat this as depending on netstandard so that we can provide netstandard1.5 and netstandard1.6 compatible
// facades since net461 was previously only compatible with netstandard1.4 and thus packages only provided netstandard1.4
// compatible facades.
private const string SystemRuntimeAssemblyName = "System.Runtime";
private static readonly Version SystemRuntimeMinVersion = new Version(4, 1, 0, 0);

/// <summary>
/// Set of reference items to analyze.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ internal static bool GetFileDependsOnNETStandard(string filePath)
var asmRefEnum = IntPtr.Zero;
var asmRefTokens = new UInt32[16];
UInt32 fetched;

var assemblyMD = new ASSEMBLYMETADATA()
{
rpLocale = IntPtr.Zero,
cchLocale = 0,
rpProcessors = IntPtr.Zero,
cProcessors = 0,
rOses = IntPtr.Zero,
cOses = 0
};

// Ensure the enum handle is closed.
try
{
Expand All @@ -72,7 +83,7 @@ internal static bool GetFileDependsOnNETStandard(string filePath)
null,
0,
out asmNameLength,
IntPtr.Zero,
ref assemblyMD,
out hashDataPtr,
out hashDataLength,
out flags);
Expand All @@ -88,7 +99,7 @@ internal static bool GetFileDependsOnNETStandard(string filePath)
assemblyNameBuffer,
(uint)assemblyNameBuffer.Capacity,
out asmNameLength,
IntPtr.Zero,
ref assemblyMD,
out hashDataPtr,
out hashDataLength,
out flags);
Expand All @@ -99,6 +110,16 @@ internal static bool GetFileDependsOnNETStandard(string filePath)
{
return true;
}

if (assemblyName.Equals(SystemRuntimeAssemblyName, StringComparison.Ordinal))
{
var assemblyVersion = new Version(assemblyMD.usMajorVersion, assemblyMD.usMinorVersion, assemblyMD.usBuildNumber, assemblyMD.usRevisionNumber);

if (assemblyVersion >= SystemRuntimeMinVersion)
{
return true;
}
}
}
} while (fetched > 0);
}
Expand Down Expand Up @@ -145,7 +166,7 @@ internal interface IMetaDataDispenser
internal interface IMetaDataAssemblyImport
{
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);
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);
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);
void GetFileProps([In] UInt32 mdFile, StringBuilder strName, UInt32 cchName, out UInt32 cchNameRequired, out IntPtr bHashData, out UInt32 cchHashBytes, out UInt32 dwFileFlags);
void GetExportedTypeProps();
void GetManifestResourceProps();
Expand All @@ -162,6 +183,39 @@ internal interface IMetaDataAssemblyImport
void CloseEnum([In] IntPtr phEnum);
void FindAssembliesByName();
}


/*
From cor.h:
typedef struct
{
USHORT usMajorVersion; // Major Version.
USHORT usMinorVersion; // Minor Version.
USHORT usBuildNumber; // Build Number.
USHORT usRevisionNumber; // Revision Number.
LPWSTR szLocale; // Locale.
ULONG cbLocale; // [IN/OUT] Size of the buffer in wide chars/Actual size.
DWORD *rProcessor; // Processor ID array.
ULONG ulProcessor; // [IN/OUT] Size of the Processor ID array/Actual # of entries filled in.
OSINFO *rOS; // OSINFO array.
ULONG ulOS; // [IN/OUT]Size of the OSINFO array/Actual # of entries filled in.
} ASSEMBLYMETADATA;
*/
[StructLayout(LayoutKind.Sequential)]
internal struct ASSEMBLYMETADATA
{
public UInt16 usMajorVersion;
public UInt16 usMinorVersion;
public UInt16 usBuildNumber;
public UInt16 usRevisionNumber;
public IntPtr rpLocale;
public UInt32 cchLocale;
public IntPtr rpProcessors;
public UInt32 cProcessors;
public IntPtr rOses;
public UInt32 cOses;
}

#endregion
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ internal static bool GetFileDependsOnNETStandard(string filePath)
{
return true;
}

if (reader.StringComparer.Equals(reference.Name, SystemRuntimeAssemblyName) &&
reference.Version >= SystemRuntimeMinVersion)
{
return true;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ public void It_does_not_include_netstandard_when_inbox(bool isSdk)
[WindowsOnlyTheory]
[InlineData(true)]
[InlineData(false)]
public void It_does_not_include_netstandard_when_libary_targets_netstandard1(bool isSdk)
public void It_does_not_include_netstandard_when_libary_targets_netstandard14(bool isSdk)
{
var testAsset = _testAssetsManager
.CopyTestAsset(GetTemplateName(isSdk))
Expand All @@ -311,7 +311,7 @@ public void It_does_not_include_netstandard_when_libary_targets_netstandard1(boo
var ns = project.Root.Name.Namespace;
var propertyGroup = project.Root.Elements(ns + "PropertyGroup").First();
var targetFrameworkProperty = propertyGroup.Element(ns + "TargetFramework");
targetFrameworkProperty.Value = "netstandard1.6";
targetFrameworkProperty.Value = "netstandard1.4";
}
});

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


[WindowsOnlyTheory]
[InlineData(true)]
[InlineData(false)]
public void It_includes_netstandard_when_libary_targets_netstandard15(bool isSdk)
{
var testAsset = _testAssetsManager
.CopyTestAsset(GetTemplateName(isSdk))
.WithSource()
.WithProjectChanges((projectPath, project) =>
{
if (IsAppProject(projectPath))
{
AddReferenceToLibrary(project, ReferenceScenario.ProjectReference);
}

if (IsLibraryProject(projectPath))
{
var ns = project.Root.Name.Namespace;
var propertyGroup = project.Root.Elements(ns + "PropertyGroup").First();
var targetFrameworkProperty = propertyGroup.Element(ns + "TargetFramework");
targetFrameworkProperty.Value = "netstandard1.5";
}
});

testAsset.Restore(Log, relativePath: AppName);

var buildCommand = new BuildCommand(Log, Path.Combine(testAsset.TestRoot, AppName));
buildCommand
.Execute()
.Should()
.Pass();

var outputDirectory = isSdk ?
buildCommand.GetOutputDirectory("net461") :
buildCommand.GetNonSDKOutputDirectory();

// NET461 didn't originally support netstandard2.0, (nor netstandard1.5 or netstandard1.6)
// Since support was added after we need to ensure we apply the shims for netstandard1.5 projects as well.

outputDirectory.Should().HaveFiles(new[] {
"netstandard.dll",
$"{AppName}.exe.config"
});
}

}
}