diff --git a/DotnetCLIVersion.txt b/DotnetCLIVersion.txt index ef8243bbefc0..38fa37cc7dee 100644 --- a/DotnetCLIVersion.txt +++ b/DotnetCLIVersion.txt @@ -1 +1 @@ -15.5.0-preview-007044 +2.1.1-preview-007089 diff --git a/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/GetDependsOnNETStandard.cs b/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/GetDependsOnNETStandard.cs index 3e7b5908c6bd..16947e761996 100644 --- a/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/GetDependsOnNETStandard.cs +++ b/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/GetDependsOnNETStandard.cs @@ -22,6 +22,17 @@ public partial class GetDependsOnNETStandard : TaskBase private const string SystemRuntimeAssemblyName = "System.Runtime"; private static readonly Version SystemRuntimeMinVersion = new Version(4, 1, 0, 0); + // Encountered conflict between 'Platform:System.IO.Compression.dll' and 'CopyLocal:C:\git\dotnet-sdk\packages\system.io.compression\4.3.0\runtimes\win\lib\net46\System.IO.Compression.dll'. Choosing 'CopyLocal:C:\git\dotnet-sdk\packages\system.io.compression\4.3.0\runtimes\win\lib\net46\System.IO.Compression.dll' because AssemblyVersion '4.1.2.0' is greater than '4.0.0.0'. + // .NET Standard facade version: 4.2.0.0 + // Encountered conflict between 'Platform:System.Net.Http.dll' and 'CopyLocal:C:\git\dotnet-sdk\packages\system.net.http\4.3.0\runtimes\win\lib\net46\System.Net.Http.dll'. Choosing 'CopyLocal:C:\git\dotnet-sdk\packages\system.net.http\4.3.0\runtimes\win\lib\net46\System.Net.Http.dll' because AssemblyVersion '4.1.1.0' is greater than '4.0.0.0'. + // .NET Standard facade version: 4.2.0.0 + + private const string SystemIOCompressionAssemblyName = "System.IO.Compression"; + private static readonly Version SystemIOCompressionMinVersion = new Version(4, 1, 0, 0); + + private const string SystemNetHttpAssemblyName = "System.Net.Http"; + private static readonly Version SystemNetHttpMinVersion = new Version(4, 1, 0, 0); + /// /// Set of reference items to analyze. /// @@ -34,13 +45,29 @@ public partial class GetDependsOnNETStandard : TaskBase [Output] public bool DependsOnNETStandard { get; set; } + /// + /// True if any of the references depend on a version of System.IO.Compression at least 4.0.1.0 (ie from a NuGet package) + /// + [Output] + public bool DependsOnNuGetCompression { get; set; } + + /// + /// True if any of the references depend on a version of System.Net.Http at least 4.0.1.0 (ie from a NuGet package) + /// + [Output] + public bool DependsOnNuGetHttp { get; set; } + protected override void ExecuteCore() { - DependsOnNETStandard = AnyReferenceDependsOnNETStandard(); + ProcessReferences(); } - private bool AnyReferenceDependsOnNETStandard() + private void ProcessReferences() { + DependsOnNETStandard = false; + DependsOnNuGetCompression = false; + DependsOnNuGetHttp = false; + foreach (var reference in References) { var referenceSourcePath = ItemUtilities.GetSourcePath(reference); @@ -49,10 +76,15 @@ private bool AnyReferenceDependsOnNETStandard() { try { - if (GetFileDependsOnNETStandard(referenceSourcePath)) - { - return true; - } + bool dependsOnNETStandard; + bool dependsOnNuGetCompression; + bool dependsOnNuGetHttp; + + GetFileDependsOn(referenceSourcePath, out dependsOnNETStandard, out dependsOnNuGetCompression, out dependsOnNuGetHttp); + + DependsOnNETStandard |= dependsOnNETStandard; + DependsOnNuGetCompression |= dependsOnNuGetCompression; + DependsOnNuGetHttp |= dependsOnNuGetHttp; } catch (Exception e) when (IsReferenceException(e)) { @@ -62,7 +94,7 @@ private bool AnyReferenceDependsOnNETStandard() } } - return false; + } // ported from MSBuild's ReferenceTable.SetPrimaryAssemblyReferenceItem diff --git a/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/GetDependsOnNETStandard.net46.cs b/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/GetDependsOnNETStandard.net46.cs index 04109268f7af..34dc20bb82b5 100644 --- a/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/GetDependsOnNETStandard.net46.cs +++ b/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/GetDependsOnNETStandard.net46.cs @@ -19,8 +19,15 @@ public partial class GetDependsOnNETStandard // - load / JIT cost of SRM and its closure // - deal with bindingRedirects/unification needed to load SRM's closure. - internal static bool GetFileDependsOnNETStandard(string filePath) + internal static void GetFileDependsOn(string filePath, + out bool dependsOnNETStandard, + out bool dependsOnNuGetCompression, + out bool dependsOnNuGetHttp) { + dependsOnNETStandard = false; + dependsOnNuGetCompression = false; + dependsOnNuGetHttp = false; + // Ported from Microsoft.Build.Tasks.AssemblyInformation if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX) { @@ -34,7 +41,22 @@ internal static bool GetFileDependsOnNETStandard(string filePath) { if (referencedAssembly.Name.Equals(NetStandardAssemblyName, StringComparison.Ordinal)) { - return true; + dependsOnNETStandard = true; + } + else if (referencedAssembly.Name.Equals(SystemRuntimeAssemblyName, StringComparison.Ordinal) && + referencedAssembly.Version >= SystemRuntimeMinVersion) + { + dependsOnNETStandard = true; + } + else if (referencedAssembly.Name.Equals(SystemIOCompressionAssemblyName, StringComparison.Ordinal) && + referencedAssembly.Version >= SystemIOCompressionMinVersion) + { + dependsOnNuGetCompression = true; + } + else if (referencedAssembly.Name.Equals(SystemNetHttpAssemblyName, StringComparison.Ordinal) && + referencedAssembly.Version >= SystemNetHttpMinVersion) + { + dependsOnNuGetHttp = true; } } } @@ -105,20 +127,26 @@ internal static bool GetFileDependsOnNETStandard(string filePath) out flags); var assemblyName = assemblyNameBuffer.ToString(); + var assemblyVersion = new Version(assemblyMD.usMajorVersion, assemblyMD.usMinorVersion, assemblyMD.usBuildNumber, assemblyMD.usRevisionNumber); if (assemblyName.Equals(NetStandardAssemblyName, StringComparison.Ordinal)) { - return true; + dependsOnNETStandard = true; } - - if (assemblyName.Equals(SystemRuntimeAssemblyName, StringComparison.Ordinal)) + else if (assemblyName.Equals(SystemRuntimeAssemblyName, StringComparison.Ordinal) && + assemblyVersion >= SystemRuntimeMinVersion) { - var assemblyVersion = new Version(assemblyMD.usMajorVersion, assemblyMD.usMinorVersion, assemblyMD.usBuildNumber, assemblyMD.usRevisionNumber); - - if (assemblyVersion >= SystemRuntimeMinVersion) - { - return true; - } + dependsOnNETStandard = true; + } + else if (assemblyName.Equals(SystemIOCompressionAssemblyName, StringComparison.Ordinal) && + assemblyVersion >= SystemIOCompressionMinVersion) + { + dependsOnNuGetCompression = true; + } + else if (assemblyName.Equals(SystemNetHttpAssemblyName, StringComparison.Ordinal) && + assemblyVersion >= SystemNetHttpMinVersion) + { + dependsOnNuGetHttp = true; } } } while (fetched > 0); @@ -141,7 +169,6 @@ internal static bool GetFileDependsOnNETStandard(string filePath) } } } - return false; } #region Interop diff --git a/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/GetDependsOnNETStandard.netstandard.cs b/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/GetDependsOnNETStandard.netstandard.cs index b7ed956079be..a86e4c8725ff 100644 --- a/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/GetDependsOnNETStandard.netstandard.cs +++ b/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/GetDependsOnNETStandard.netstandard.cs @@ -11,8 +11,14 @@ namespace Microsoft.NET.Build.Tasks { public partial class GetDependsOnNETStandard { - internal static bool GetFileDependsOnNETStandard(string filePath) + internal static void GetFileDependsOn(string filePath, + out bool dependsOnNETStandard, + out bool dependsOnNuGetCompression, + out bool dependsOnNuGetHttp) { + dependsOnNETStandard = false; + dependsOnNuGetCompression = false; + dependsOnNuGetHttp = false; using (var assemblyStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read)) using (PEReader peReader = new PEReader(assemblyStream, PEStreamOptions.LeaveOpen)) { @@ -27,20 +33,30 @@ internal static bool GetFileDependsOnNETStandard(string filePath) if (reader.StringComparer.Equals(reference.Name, NetStandardAssemblyName)) { - return true; + dependsOnNETStandard = true; } if (reader.StringComparer.Equals(reference.Name, SystemRuntimeAssemblyName) && reference.Version >= SystemRuntimeMinVersion) { - return true; + dependsOnNETStandard = true; + } + + if (reader.StringComparer.Equals(reference.Name, SystemIOCompressionAssemblyName) && + reference.Version >= SystemIOCompressionMinVersion) + { + dependsOnNuGetCompression = true; + } + + if (reader.StringComparer.Equals(reference.Name, SystemNetHttpAssemblyName) && + reference.Version >= SystemNetHttpMinVersion) + { + dependsOnNuGetHttp = true; } } } } } - - return false; } } } diff --git a/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/Microsoft.NET.Build.Extensions.Tasks.csproj b/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/Microsoft.NET.Build.Extensions.Tasks.csproj index 829b05771742..db4043f4f660 100644 --- a/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/Microsoft.NET.Build.Extensions.Tasks.csproj +++ b/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/Microsoft.NET.Build.Extensions.Tasks.csproj @@ -93,7 +93,7 @@ - + <_CopyLocalButNotPublished Include="@(AllCopyLocalItems)" Exclude="@(ResolvedAssembliesToPublish)" /> diff --git a/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/msbuildExtensions/Microsoft/Microsoft.NET.Build.Extensions/Microsoft.NET.Build.Extensions.NETFramework.targets b/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/msbuildExtensions/Microsoft/Microsoft.NET.Build.Extensions/Microsoft.NET.Build.Extensions.NETFramework.targets index 9dac343b3fbb..94450674381d 100644 --- a/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/msbuildExtensions/Microsoft/Microsoft.NET.Build.Extensions/Microsoft.NET.Build.Extensions.NETFramework.targets +++ b/src/Tasks/Microsoft.NET.Build.Extensions.Tasks/msbuildExtensions/Microsoft/Microsoft.NET.Build.Extensions/Microsoft.NET.Build.Extensions.NETFramework.targets @@ -44,10 +44,20 @@ Copyright (c) .NET Foundation. All rights reserved. true + + + + + <_RunGetDependsOnNETStandard Condition="'$(DependsOnNETStandard)' == '' AND '$(NETStandardInbox)' != 'true'">true + + <_RunGetDependsOnNETStandard Condition="'$(_TargetFrameworkVersionWithoutV)' == '4.7.1'">true + - + + @@ -57,8 +67,20 @@ Copyright (c) .NET Foundation. All rights reserved. + + + <_NETStandardLibraryNETFrameworkLib Include="$(MSBuildThisFileDirectory)\net461\lib\System.Net.Http.dll" Condition="'$(DependsOnNuGetHttp)' == 'true' Or '$(DependsOnNETStandard)' == 'true'"/> + <_NETStandardLibraryNETFrameworkLib Include="$(MSBuildThisFileDirectory)\net461\lib\System.IO.Compression.dll" Condition="'$(DependsOnNuGetCompression)' == 'true' Or '$(DependsOnNETStandard)' == 'true'"/> + + - + <_NETStandardLibraryNETFrameworkLib Condition="'$(_TargetFrameworkVersionWithoutV)' >= '4.7'" Include="$(MSBuildThisFileDirectory)net47\lib\*.dll" /> <_NETStandardLibraryNETFrameworkLib Condition="'$(_TargetFrameworkVersionWithoutV)' >= '4.6.2'" @@ -67,7 +89,9 @@ Copyright (c) .NET Foundation. All rights reserved. <_NETStandardLibraryNETFrameworkLib Condition="'$(_TargetFrameworkVersionWithoutV)' >= '4.6.1'" Include="$(MSBuildThisFileDirectory)net461\lib\*.dll" Exclude="@(_NETStandardLibraryNETFrameworkLib->'$(MSBuildThisFileDirectory)net461\lib\%(FileName).dll')" /> - + + + - + <_CopyLocalButNotPublished Include="@(AllCopyLocalItems)" Exclude="@(ResolvedAssembliesToPublish)" /> diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToTargetNet471.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToTargetNet471.cs index e35c5b50aee2..5489ac277d4e 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToTargetNet471.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToTargetNet471.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using FluentAssertions; +using Microsoft.Build.Utilities; using Microsoft.NET.TestFramework; using Microsoft.NET.TestFramework.Assertions; using Microsoft.NET.TestFramework.Commands; @@ -10,6 +11,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using System.Text; using Xunit; using Xunit.Abstractions; @@ -24,9 +26,15 @@ public GivenThatWeWantToTargetNet471(ITestOutputHelper log) : base(log) { } - [WindowsOnlyFact(Skip = "https://github.com/dotnet/sdk/issues/1625")] + [Fact] public void It_builds_a_net471_app() { + // https://github.com/dotnet/sdk/issues/1625 + if (!Net471ReferenceAssembliesAreInstalled()) + { + return; + } + var testProject = new TestProject() { Name = "Net471App", @@ -55,9 +63,15 @@ public void It_builds_a_net471_app() }); } - [WindowsOnlyFact(Skip = "https://github.com/dotnet/sdk/issues/1625")] + [Fact] public void It_builds_a_net471_app_referencing_netstandard20() { + // https://github.com/dotnet/sdk/issues/1625 + if (!Net471ReferenceAssembliesAreInstalled()) + { + return; + } + var testProject = new TestProject() { Name = "Net471App_Referencing_NetStandard20", @@ -94,12 +108,21 @@ public void It_builds_a_net471_app_referencing_netstandard20() $"{testProject.Name}.pdb", $"{netStandardProject.Name}.dll", $"{netStandardProject.Name}.pdb", + + "System.Net.Http.dll", + "System.IO.Compression.dll", }); } - [WindowsOnlyFact(Skip = "https://github.com/dotnet/sdk/issues/1625")] + [Fact] public void It_does_not_include_facades_from_nuget_packages() { + // https://github.com/dotnet/sdk/issues/1625 + if (!Net471ReferenceAssembliesAreInstalled()) + { + return; + } + var testProject = new TestProject() { Name = "Net471_NuGetFacades", @@ -128,7 +151,6 @@ public void It_does_not_include_facades_from_nuget_packages() $"{testProject.Name}.exe", $"{testProject.Name}.pdb", - // Remove these two once https://github.com/dotnet/sdk/issues/1647 is fixed "System.Net.Http.dll", "System.IO.Compression.dll", @@ -136,5 +158,148 @@ public void It_does_not_include_facades_from_nuget_packages() "System.Diagnostics.DiagnosticSource.dll", }); } + + [Theory] + [InlineData("netstandard1.4")] + [InlineData("netstandard2.0")] + public void It_uses_updated_httpClient_and_compression(string netstandardVersion) + { + // https://github.com/dotnet/sdk/issues/1625 + if (!Net471ReferenceAssembliesAreInstalled()) + { + return; + } + + var testProject = new TestProject() + { + Name = "Net471_HttpClient", + TargetFrameworks = "net471", + IsSdkProject = true, + IsExe = true + }; + + testProject.References.Add("System.Net.Http"); + testProject.References.Add("System.IO.Compression"); + + var referencedProject = new TestProject() + { + Name = "NetStandardProject", + TargetFrameworks = netstandardVersion, + IsSdkProject = true, + IsExe = false + }; + + testProject.ReferencedProjects.Add(referencedProject); + + testProject.SourceFiles["Program.cs"] = @" +using System; +using System.Net.Http; +using System.Net.Http.Headers; + +public static class Program +{ + public static void Main() + { + HttpClient httpClient = Class1.GetHttpClient(); + + // AuthenticationHeaderValue is IClonable in .NET Framework and in version 4.2.0.0 of the contract + // (which is the version from ImplicitlyExpandNETStandardFacades), but not in prior versions of the + // contract, which would come from the package closure of 1.x versions of NETStandard.Library + ICloneable cloneable = new AuthenticationHeaderValue(""scheme""); + } +}"; + + referencedProject.SourceFiles["Class1.cs"] = @" +using System.Net.Http; + +public class Class1 +{ + public static HttpClient GetHttpClient() + { + return new HttpClient(); + } +} +"; + + var testAsset = _testAssetsManager.CreateTestProject(testProject, testProject.Name, netstandardVersion) + .Restore(Log, testProject.Name); + + var buildCommand = new BuildCommand(Log, Path.Combine(testAsset.TestRoot, testProject.Name)); + + buildCommand + .Execute("/v:normal") + .Should() + .Pass() + .And.NotHaveStdOutContaining("MSB3277") // MSB3277: Found conflicts between different versions of the same dependent assembly that could not be resolved. + .And.NotHaveStdOutContaining("Could not determine"); + + var outputDirectory = buildCommand.GetOutputDirectory(testProject.TargetFrameworks); + + outputDirectory.Should().HaveFiles(new[] + { + "System.Net.Http.dll", + "System.IO.Compression.dll" + }); + + var httpAssemblyName = AssemblyName.GetAssemblyName(Path.Combine(outputDirectory.FullName, "System.Net.Http.dll")); + httpAssemblyName.Version.Should().Be(new Version(4, 2, 0, 0)); + + var compressionAssemblyName = AssemblyName.GetAssemblyName(Path.Combine(outputDirectory.FullName, "System.IO.Compression.dll")); + httpAssemblyName.Version.Should().Be(new Version(4, 2, 0, 0)); + } + + [Fact] + public void It_does_not_include_httpClient_and_compression_if_netstandard_isnt_referenced() + { + // https://github.com/dotnet/sdk/issues/1625 + if (!Net471ReferenceAssembliesAreInstalled()) + { + return; + } + + var testProject = new TestProject() + { + Name = "Net471_No_NetStandard", + TargetFrameworks = "net471", + IsSdkProject = true, + IsExe = true + }; + + testProject.References.Add("System.Net.Http"); + testProject.References.Add("System.IO.Compression"); + + var testAsset = _testAssetsManager.CreateTestProject(testProject, testProject.Name) + .Restore(Log, testProject.Name); + + var buildCommand = new BuildCommand(Log, Path.Combine(testAsset.TestRoot, testProject.Name)); + + buildCommand + .Execute("/v:normal") + .Should() + .Pass() + .And.NotHaveStdOutContaining("MSB3277") // MSB3277: Found conflicts between different versions of the same dependent assembly that could not be resolved. + .And.NotHaveStdOutContaining("Could not determine"); + + var outputDirectory = buildCommand.GetOutputDirectory(testProject.TargetFrameworks); + + outputDirectory.Should().NotHaveFiles(new[] + { + "System.Net.Http.dll", + "System.IO.Compression.dll" + }); + } + + static bool Net471ReferenceAssembliesAreInstalled() + { + var net461referenceAssemblies = ToolLocationHelper.GetPathToDotNetFrameworkReferenceAssemblies(TargetDotNetFrameworkVersion.Version461); + if (net461referenceAssemblies == null) + { + // 4.6.1 reference assemblies not found, assume that 4.7.1 isn't available either + return false; + } + var net471referenceAssemblies = Path.Combine(new DirectoryInfo(net461referenceAssemblies).Parent.FullName, "v4.7.1"); + return Directory.Exists(net471referenceAssemblies); + } + } }