Merge remote-tracking branch 'dotnet/release/2.1.5xx' into merges/release/2.1.5xx-to-release/2.2.1xx
* dotnet/release/2.1.5xx: MSBuild 15.9.8-preview MSBuild SDK Resolver Improvements (#9547) Remove fallback folder Remove redundent call, ensure no apt list Update links in the readme to release/2.1.5xx MSBuild 15.9.0-preview-000006 Use prebuild image Revert "Disabled MSBuildTreatWarningsAsErrors" Disabled MSBuildTreatWarningsAsErrors Add back: "PUBLISH_NUPKG_TO_BLOB_FEED" to manage the no-suffix builds in 2.1 Adding the 2.1.3 runtime blob feed as a feed for the CLI Update CLI branding to 2.1.402 Updated test TestPlatform 15.9.0-preview Insertion Update CLI branding to 2.1.203. Fix warnings-as-errors in Linux packaging Don't let crossgen warnings become msbuild warnings-as-errors Fix build warning and treat msbuild warnings as errors Conflicts: README.md build/DependencyVersions.props build/package/Installer.DEB.proj
This commit is contained in:
commit
057098046f
21 changed files with 931 additions and 260 deletions
|
@ -1,8 +1,9 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project>
|
||||||
|
|
||||||
<!-- This test asset needs to import the general dir.props in order to get the SdkNugetVersion property.
|
<!-- This test asset needs to import the general dir.props in order to get the SdkNugetVersion property.
|
||||||
This is why it also needs to explicitly set DisableImplicitFrameworkReferences to false. -->
|
This is why it also needs to explicitly set DisableImplicitFrameworkReferences to false. -->
|
||||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
|
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
|
||||||
|
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.props" />
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<VersionPrefix>1.0.0-rc</VersionPrefix>
|
<VersionPrefix>1.0.0-rc</VersionPrefix>
|
||||||
|
@ -23,4 +24,5 @@
|
||||||
<PackageReference Include="System.Linq" Version="4.3.0" />
|
<PackageReference Include="System.Linq" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.targets" />
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -33,5 +33,6 @@
|
||||||
<NoWarn>NU1701;NU5104</NoWarn>
|
<NoWarn>NU1701;NU5104</NoWarn>
|
||||||
|
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
<MSBuildTreatWarningsAsErrors>true</MSBuildTreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -11,8 +11,11 @@
|
||||||
<DotnetUserSecretsPackageVersion>2.1.1</DotnetUserSecretsPackageVersion>
|
<DotnetUserSecretsPackageVersion>2.1.1</DotnetUserSecretsPackageVersion>
|
||||||
<DotnetWatchPackageVersion>2.1.1</DotnetWatchPackageVersion>
|
<DotnetWatchPackageVersion>2.1.1</DotnetWatchPackageVersion>
|
||||||
<MicrosoftNETCoreAppPackageVersion>2.2.0-preview1-26609-02</MicrosoftNETCoreAppPackageVersion>
|
<MicrosoftNETCoreAppPackageVersion>2.2.0-preview1-26609-02</MicrosoftNETCoreAppPackageVersion>
|
||||||
<MicrosoftNETCoreDotNetHostResolverPackageVersion>$(MicrosoftNETCoreAppPackageVersion)</MicrosoftNETCoreDotNetHostResolverPackageVersion>
|
|
||||||
<MicrosoftBuildPackageVersion>15.8.166</MicrosoftBuildPackageVersion>
|
<!-- https://github.com/dotnet/cli/issues/9851 -->
|
||||||
|
<MicrosoftNETCoreDotNetHostResolverPackageVersion_ForMSBuildSdkResolverOnly>3.0.0-preview1-26816-04</MicrosoftNETCoreDotNetHostResolverPackageVersion_ForMSBuildSdkResolverOnly>
|
||||||
|
|
||||||
|
<MicrosoftBuildPackageVersion>15.9.0-preview-000008</MicrosoftBuildPackageVersion>
|
||||||
<MicrosoftBuildFrameworkPackageVersion>$(MicrosoftBuildPackageVersion)</MicrosoftBuildFrameworkPackageVersion>
|
<MicrosoftBuildFrameworkPackageVersion>$(MicrosoftBuildPackageVersion)</MicrosoftBuildFrameworkPackageVersion>
|
||||||
<MicrosoftBuildRuntimePackageVersion>$(MicrosoftBuildPackageVersion)</MicrosoftBuildRuntimePackageVersion>
|
<MicrosoftBuildRuntimePackageVersion>$(MicrosoftBuildPackageVersion)</MicrosoftBuildRuntimePackageVersion>
|
||||||
<MicrosoftBuildLocalizationPackageVersion>$(MicrosoftBuildPackageVersion)</MicrosoftBuildLocalizationPackageVersion>
|
<MicrosoftBuildLocalizationPackageVersion>$(MicrosoftBuildPackageVersion)</MicrosoftBuildLocalizationPackageVersion>
|
||||||
|
@ -53,7 +56,7 @@
|
||||||
<NuGetProjectModelPackageVersion>$(NuGetBuildTasksPackageVersion)</NuGetProjectModelPackageVersion>
|
<NuGetProjectModelPackageVersion>$(NuGetBuildTasksPackageVersion)</NuGetProjectModelPackageVersion>
|
||||||
<NuGetVersioningPackageVersion>$(NuGetBuildTasksPackageVersion)</NuGetVersioningPackageVersion>
|
<NuGetVersioningPackageVersion>$(NuGetBuildTasksPackageVersion)</NuGetVersioningPackageVersion>
|
||||||
<MicrosoftBuildNuGetSdkResolverPackageVersion>$(NuGetBuildTasksPackageVersion)</MicrosoftBuildNuGetSdkResolverPackageVersion>
|
<MicrosoftBuildNuGetSdkResolverPackageVersion>$(NuGetBuildTasksPackageVersion)</MicrosoftBuildNuGetSdkResolverPackageVersion>
|
||||||
<MicrosoftNETTestSdkPackageVersion>15.8.0</MicrosoftNETTestSdkPackageVersion>
|
<MicrosoftNETTestSdkPackageVersion>15.9.0-preview-20180807-05</MicrosoftNETTestSdkPackageVersion>
|
||||||
<MicrosoftTestPlatformCLIPackageVersion>$(MicrosoftNETTestSdkPackageVersion)</MicrosoftTestPlatformCLIPackageVersion>
|
<MicrosoftTestPlatformCLIPackageVersion>$(MicrosoftNETTestSdkPackageVersion)</MicrosoftTestPlatformCLIPackageVersion>
|
||||||
<MicrosoftTestPlatformBuildPackageVersion>$(MicrosoftNETTestSdkPackageVersion)</MicrosoftTestPlatformBuildPackageVersion>
|
<MicrosoftTestPlatformBuildPackageVersion>$(MicrosoftNETTestSdkPackageVersion)</MicrosoftTestPlatformBuildPackageVersion>
|
||||||
<XliffTasksPackageVersion>0.2.0-beta-000042</XliffTasksPackageVersion>
|
<XliffTasksPackageVersion>0.2.0-beta-000042</XliffTasksPackageVersion>
|
||||||
|
@ -78,7 +81,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<BuildTasksFeedToolVersion>2.1.0-prerelease-02430-04</BuildTasksFeedToolVersion>
|
<BuildTasksFeedToolVersion>2.1.0-prerelease-02430-04</BuildTasksFeedToolVersion>
|
||||||
<VersionToolsVersion>$(BuildTasksFeedToolVersion)</VersionToolsVersion>
|
<VersionToolsVersion>$(BuildTasksFeedToolVersion)</VersionToolsVersion>
|
||||||
<DotnetDebToolVersion>2.0.0-preview2-25331-01</DotnetDebToolVersion>
|
<DotnetDebToolVersion>2.0.0</DotnetDebToolVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
<add key="linux-musl-bootstrap-feed" value="https://dotnetfeed.blob.core.windows.net/orchestrated-release-2-1/20180420-03/aspnet-inputs/index.json" />
|
<add key="linux-musl-bootstrap-feed" value="https://dotnetfeed.blob.core.windows.net/orchestrated-release-2-1/20180420-03/aspnet-inputs/index.json" />
|
||||||
<add key="dotnet-msbuild" value="https://dotnet.myget.org/F/msbuild/api/v3/index.json" />
|
<add key="dotnet-msbuild" value="https://dotnet.myget.org/F/msbuild/api/v3/index.json" />
|
||||||
<add key="myget-vstest" value="https://dotnet.myget.org/F/vstest/api/v3/index.json" />
|
<add key="myget-vstest" value="https://dotnet.myget.org/F/vstest/api/v3/index.json" />
|
||||||
|
<add key="BlobFeed-2.1.3-runtime" value="https://dotnetfeed.blob.core.windows.net/orchestrated-release-2-1/20180725-02/final/index.json"/>
|
||||||
]]>
|
]]>
|
||||||
</NugetConfigCLIFeeds>
|
</NugetConfigCLIFeeds>
|
||||||
|
|
||||||
|
|
|
@ -94,15 +94,15 @@
|
||||||
UseHardlinksIfPossible="False" />
|
UseHardlinksIfPossible="False" />
|
||||||
|
|
||||||
<!-- Proactively remove all possible Shared Framework and Debian Packages -->
|
<!-- Proactively remove all possible Shared Framework and Debian Packages -->
|
||||||
<Exec Command="sudo dpkg -r $(SdkDebianPackageName)" />
|
<Exec Command="sudo dpkg -r $(SdkDebianPackageName)" IgnoreStandardErrorWarningFormat="true" />
|
||||||
<Exec Command="sudo dpkg -r $(AspNetCoreSharedFxDebianPackageName)" />
|
<Exec Command="sudo dpkg -r $(AspNetCoreSharedFxDebianPackageName)" IgnoreStandardErrorWarningFormat="true" />
|
||||||
<Exec Command="sudo dpkg -r $(AspNetCoreSharedFxDebianPackageFileName)" />
|
<Exec Command="sudo dpkg -r $(AspNetCoreSharedFxDebianPackageFileName)" IgnoreStandardErrorWarningFormat="true" />
|
||||||
<Exec Command="sudo dpkg -r $(SharedFxDebianPackageName)" />
|
<Exec Command="sudo dpkg -r $(SharedFxDebianPackageName)" IgnoreStandardErrorWarningFormat="true" />
|
||||||
<Exec Command="sudo dpkg -r $(SharedFxDebianPackageFileName)" />
|
<Exec Command="sudo dpkg -r $(SharedFxDebianPackageFileName)" IgnoreStandardErrorWarningFormat="true" />
|
||||||
<Exec Command="sudo dpkg -r $(HostFxrDebianPackageName)" />
|
<Exec Command="sudo dpkg -r $(HostFxrDebianPackageName)" IgnoreStandardErrorWarningFormat="true" />
|
||||||
<Exec Command="sudo dpkg -r $(HostFxrDebianPackageFileName)" />
|
<Exec Command="sudo dpkg -r $(HostFxrDebianPackageFileName)" IgnoreStandardErrorWarningFormat="true" />
|
||||||
<Exec Command="sudo dpkg -r $(HostDebianPackageName)" />
|
<Exec Command="sudo dpkg -r $(HostDebianPackageName)" IgnoreStandardErrorWarningFormat="true" />
|
||||||
<Exec Command="sudo dpkg -r $(RuntimeDepsPackageName)" />
|
<Exec Command="sudo dpkg -r $(RuntimeDepsPackageName)" IgnoreStandardErrorWarningFormat="true" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="TestSdkDeb"
|
<Target Name="TestSdkDeb"
|
||||||
|
@ -132,12 +132,15 @@
|
||||||
ToolPath="$(DebianInstalledDirectory)" />
|
ToolPath="$(DebianInstalledDirectory)" />
|
||||||
|
|
||||||
<!-- Clean up Packages -->
|
<!-- Clean up Packages -->
|
||||||
<Exec Command="sudo dpkg -r $(SdkDebianPackageName)" />
|
<!-- The following line is needed. So it won't warning dotnet folder is not empty after uninstall -->
|
||||||
<Exec Command="sudo dpkg -r $(AspNetCoreSharedFxDebianPackageName)" />
|
<Exec Command="sudo rm -rf /usr/share/dotnet/sdk/NuGetFallbackFolder" />
|
||||||
<Exec Command="sudo dpkg -r $(SharedFxDebianPackageName)" />
|
|
||||||
<Exec Command="sudo dpkg -r $(HostFxrDebianPackageName)" />
|
<Exec Command="sudo dpkg -r $(SdkDebianPackageName)" IgnoreStandardErrorWarningFormat="true" />
|
||||||
<Exec Command="sudo dpkg -r $(HostDebianPackageName)" />
|
<Exec Command="sudo dpkg -r $(AspNetCoreSharedFxDebianPackageName)" IgnoreStandardErrorWarningFormat="true" />
|
||||||
<Exec Command="sudo dpkg -r $(RuntimeDepsPackageName)" />
|
<Exec Command="sudo dpkg -r $(SharedFxDebianPackageName)" IgnoreStandardErrorWarningFormat="true" />
|
||||||
|
<Exec Command="sudo dpkg -r $(HostFxrDebianPackageName)" IgnoreStandardErrorWarningFormat="true" />
|
||||||
|
<Exec Command="sudo dpkg -r $(HostDebianPackageName)" IgnoreStandardErrorWarningFormat="true" />
|
||||||
|
<Exec Command="sudo dpkg -r $(RuntimeDepsPackageName)" IgnoreStandardErrorWarningFormat="true" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="PrepareDotnetDebDirectories">
|
<Target Name="PrepareDotnetDebDirectories">
|
||||||
|
@ -177,8 +180,9 @@
|
||||||
<Target Name="TestDebuild">
|
<Target Name="TestDebuild">
|
||||||
<Message Text="Don't remove this" />
|
<Message Text="Don't remove this" />
|
||||||
|
|
||||||
<!-- run Debuild -->
|
<!-- run Debuild -->
|
||||||
<Exec Command="/usr/bin/env debuild -h" ContinueOnError="true">
|
<!-- NB: IgnoreExitCode prevents Exec from failing, but does not prevent us from retrieving the exit code. -->
|
||||||
|
<Exec Command="/usr/bin/env debuild -h > /dev/null 2>&1" IgnoreExitCode="true">
|
||||||
<Output TaskParameter="ExitCode" PropertyName="DebuildExitCode" />
|
<Output TaskParameter="ExitCode" PropertyName="DebuildExitCode" />
|
||||||
</Exec>
|
</Exec>
|
||||||
|
|
||||||
|
|
|
@ -197,7 +197,8 @@
|
||||||
<Target Name="TestFPMTool">
|
<Target Name="TestFPMTool">
|
||||||
|
|
||||||
<!-- run FPM -->
|
<!-- run FPM -->
|
||||||
<Exec Command="fpm --help > /dev/null" ContinueOnError="True">
|
<!-- NB: IgnoreExitCode prevents Exec from failing, but does not prevent us from retrieving the exit code. -->
|
||||||
|
<Exec Command="fpm --help > /dev/null 2>&1" IgnoreExitCode="True">
|
||||||
<Output TaskParameter="ExitCode" PropertyName="FPMExitCode"/>
|
<Output TaskParameter="ExitCode" PropertyName="FPMExitCode"/>
|
||||||
</Exec>
|
</Exec>
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="PublishNupkgToBlobFeed" DependsOnTargets="GatherNupkgsForPublishing">
|
<Target Name="PublishNupkgToBlobFeed" Condition=" '$(PUBLISH_NUPKG_TO_BLOB_FEED)' == 'true'" DependsOnTargets="GatherNupkgsForPublishing">
|
||||||
<Error Condition="'$(BlobFeedCloudDropAccessToken)' == ''" Text="Missing property BlobFeedCloudDropAccessToken." />
|
<Error Condition="'$(BlobFeedCloudDropAccessToken)' == ''" Text="Missing property BlobFeedCloudDropAccessToken." />
|
||||||
<Error Condition="'$(BlobFeedExpectedURL)' == ''" Text="Missing property BlobFeedExpectedURL." />
|
<Error Condition="'$(BlobFeedExpectedURL)' == ''" Text="Missing property BlobFeedExpectedURL." />
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,38 @@ namespace Microsoft.DotNet.Build.Tasks
|
||||||
|
|
||||||
protected override MessageImportance StandardOutputLoggingImportance
|
protected override MessageImportance StandardOutputLoggingImportance
|
||||||
{
|
{
|
||||||
get { return MessageImportance.High; } // or else the output doesn't get logged by default
|
// Default is low, but we want to see output at normal verbosity.
|
||||||
|
get { return MessageImportance.Normal; }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override MessageImportance StandardErrorLoggingImportance
|
||||||
|
{
|
||||||
|
// This turns stderr messages into msbuild errors below.
|
||||||
|
get { return MessageImportance.High; }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LogEventsFromTextOutput(string singleLine, MessageImportance messageImportance)
|
||||||
|
{
|
||||||
|
// Crossgen's error/warning formatting is inconsistent and so we do
|
||||||
|
// not use the "canonical error format" handling of base.
|
||||||
|
//
|
||||||
|
// Furthermore, we don't want to log crossgen warnings as msbuild
|
||||||
|
// warnings because we cannot prevent them and they are only
|
||||||
|
// occasionally formatted as something that base would recognize as
|
||||||
|
// a canonically formatted warning anyway.
|
||||||
|
//
|
||||||
|
// One thing that is consistent is that crossgne errors go to stderr
|
||||||
|
// and everything else goes to stdout. Above, we set stderr to high
|
||||||
|
// importance above, and stdout to normal. So we can use that here
|
||||||
|
// to distinguish between errors and messages.
|
||||||
|
if (messageImportance == MessageImportance.High)
|
||||||
|
{
|
||||||
|
Log.LogError(singleLine);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.LogMessage(messageImportance, singleLine);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string GenerateFullPathToTool()
|
protected override string GenerateFullPathToTool()
|
||||||
|
|
|
@ -4,47 +4,16 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
# Dockerfile that creates a container suitable to build dotnet-cli
|
# Dockerfile that creates a container suitable to build dotnet-cli
|
||||||
FROM debian:jessie
|
FROM microsoft/dotnet-buildtools-prereqs:debian-8.2-debpkg-d770b8b-20180628122423
|
||||||
|
|
||||||
# Misc Dependencies for build
|
# Misc Dependencies for build
|
||||||
RUN apt-get update && \
|
RUN rm -rf /var/lib/apt/lists/* && \
|
||||||
|
apt-get update && \
|
||||||
apt-get -qqy install \
|
apt-get -qqy install \
|
||||||
curl \
|
|
||||||
unzip \
|
|
||||||
gettext \
|
|
||||||
sudo && \
|
sudo && \
|
||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# This could become a "microsoft/coreclr" image, since it just installs the dependencies for CoreCLR (and stdlib)
|
|
||||||
RUN apt-get update &&\
|
|
||||||
apt-get -qqy install \
|
|
||||||
libunwind8 \
|
|
||||||
libkrb5-3 \
|
|
||||||
libicu52 \
|
|
||||||
liblttng-ust0 \
|
|
||||||
libssl1.0.0 \
|
|
||||||
zlib1g \
|
|
||||||
libuuid1 && \
|
|
||||||
apt-get clean && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Install Build Prereqs
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get -qqy install \
|
|
||||||
debhelper \
|
|
||||||
build-essential \
|
|
||||||
devscripts \
|
|
||||||
git \
|
|
||||||
cmake \
|
|
||||||
clang-3.5 && \
|
|
||||||
apt-get clean && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Use clang as c++ compiler
|
|
||||||
RUN update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-3.5 100
|
|
||||||
RUN update-alternatives --set c++ /usr/bin/clang++-3.5
|
|
||||||
|
|
||||||
# Setup User to match Host User, and give superuser permissions
|
# Setup User to match Host User, and give superuser permissions
|
||||||
ARG USER_ID=0
|
ARG USER_ID=0
|
||||||
RUN useradd -m code_executor -u ${USER_ID} -g sudo
|
RUN useradd -m code_executor -u ${USER_ID} -g sudo
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Microsoft.DotNet.MSBuildSdkResolver
|
|
||||||
{
|
|
||||||
internal static partial class Interop
|
|
||||||
{
|
|
||||||
internal static string hostfxr_resolve_sdk(string exe_dir, string working_dir)
|
|
||||||
{
|
|
||||||
var buffer = new StringBuilder(capacity: 64);
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
int size = hostfxr_resolve_sdk(exe_dir, working_dir, buffer, buffer.Capacity);
|
|
||||||
if (size <= 0)
|
|
||||||
{
|
|
||||||
Debug.Assert(size == 0);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size <= buffer.Capacity)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.Capacity = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
||||||
|
|
||||||
#if NET46
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Microsoft.DotNet.MSBuildSdkResolver
|
|
||||||
{
|
|
||||||
internal static partial class Interop
|
|
||||||
{
|
|
||||||
internal static readonly bool RunningOnWindows = true;
|
|
||||||
|
|
||||||
static Interop()
|
|
||||||
{
|
|
||||||
PreloadLibrary("hostfxr.dll");
|
|
||||||
}
|
|
||||||
|
|
||||||
// MSBuild SDK resolvers are required to be AnyCPU, but we have a native dependency and .NETFramework does not
|
|
||||||
// have a built-in facility for dynamically loading user native dlls for the appropriate platform. We therefore
|
|
||||||
// preload the version with the correct architecture (from a corresponding sub-folder relative to us) on static
|
|
||||||
// construction so that subsequent P/Invokes can find it.
|
|
||||||
private static void PreloadLibrary(string dllFileName)
|
|
||||||
{
|
|
||||||
string basePath = Path.GetDirectoryName(typeof(Interop).Assembly.Location);
|
|
||||||
string architecture = IntPtr.Size == 8 ? "x64" : "x86";
|
|
||||||
string dllPath = Path.Combine(basePath, architecture, dllFileName);
|
|
||||||
|
|
||||||
// return value is intentially ignored as we let the subsequent P/Invokes fail naturally.
|
|
||||||
LoadLibraryExW(dllPath, IntPtr.Zero, LOAD_WITH_ALTERED_SEARCH_PATH);
|
|
||||||
}
|
|
||||||
|
|
||||||
// lpFileName passed to LoadLibraryEx must be a full path.
|
|
||||||
private const int LOAD_WITH_ALTERED_SEARCH_PATH = 0x8;
|
|
||||||
|
|
||||||
[DllImport("kernel32", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
|
|
||||||
private static extern IntPtr LoadLibraryExW(string lpFileName, IntPtr hFile, int dwFlags);
|
|
||||||
|
|
||||||
[DllImport("hostfxr", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
private static extern int hostfxr_resolve_sdk(string exe_dir, string working_dir, [Out] StringBuilder buffer, int buffer_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // NET46
|
|
|
@ -1,53 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
||||||
|
|
||||||
// NOTE: the NET46 build ships with Visual Studio/desktop msbuild on Windows.
|
|
||||||
// The netstandard1.5 adaptation here acts a proof-of-concept for cross-platform
|
|
||||||
// portability of the underlying hostfxr API and gives us build and test coverage
|
|
||||||
// on non-Windows machines. It also ships with msbuild on Mono.
|
|
||||||
#if NETSTANDARD2_0
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Microsoft.DotNet.MSBuildSdkResolver
|
|
||||||
{
|
|
||||||
internal static partial class Interop
|
|
||||||
{
|
|
||||||
internal static readonly bool RunningOnWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
|
||||||
|
|
||||||
internal static string realpath(string path)
|
|
||||||
{
|
|
||||||
var ptr = unix_realpath(path, IntPtr.Zero);
|
|
||||||
var result = Marshal.PtrToStringAnsi(ptr); // uses UTF8 on Unix
|
|
||||||
unix_free(ptr);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int hostfxr_resolve_sdk(string exe_dir, string working_dir, [Out] StringBuilder buffer, int buffer_size)
|
|
||||||
{
|
|
||||||
// hostfxr string encoding is platform -specific so dispatch to the
|
|
||||||
// appropriately annotated P/Invoke for the current platform.
|
|
||||||
return RunningOnWindows
|
|
||||||
? windows_hostfxr_resolve_sdk(exe_dir, working_dir, buffer, buffer_size)
|
|
||||||
: unix_hostfxr_resolve_sdk(exe_dir, working_dir, buffer, buffer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
[DllImport("hostfxr", EntryPoint = nameof(hostfxr_resolve_sdk), CharSet = CharSet.Unicode, ExactSpelling=true, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
private static extern int windows_hostfxr_resolve_sdk(string exe_dir, string working_dir, [Out] StringBuilder buffer, int buffer_size);
|
|
||||||
|
|
||||||
// CharSet.Ansi is UTF8 on Unix
|
|
||||||
[DllImport("hostfxr", EntryPoint = nameof(hostfxr_resolve_sdk), CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
private static extern int unix_hostfxr_resolve_sdk(string exe_dir, string working_dir, [Out] StringBuilder buffer, int buffer_size);
|
|
||||||
|
|
||||||
// CharSet.Ansi is UTF8 on Unix
|
|
||||||
[DllImport("libc", EntryPoint = nameof(realpath), CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
private static extern IntPtr unix_realpath(string path, IntPtr buffer);
|
|
||||||
|
|
||||||
[DllImport("libc", EntryPoint = "free", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
private static extern void unix_free(IntPtr ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // NETSTANDARD2_0
|
|
135
src/Microsoft.DotNet.MSBuildSdkResolver/Interop.cs
Normal file
135
src/Microsoft.DotNet.MSBuildSdkResolver/Interop.cs
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.MSBuildSdkResolver
|
||||||
|
{
|
||||||
|
internal static partial class Interop
|
||||||
|
{
|
||||||
|
internal static readonly bool RunningOnWindows =
|
||||||
|
#if NET46
|
||||||
|
// Not using RuntimeInformation on NET46 to avoid non-in-box framework API,
|
||||||
|
// which create deployment problems for the resolver.
|
||||||
|
Path.DirectorySeparatorChar == '\\';
|
||||||
|
#else
|
||||||
|
RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static Interop()
|
||||||
|
{
|
||||||
|
if (RunningOnWindows)
|
||||||
|
{
|
||||||
|
PreloadWindowsLibrary("hostfxr.dll");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MSBuild SDK resolvers are required to be AnyCPU, but we have a native dependency and .NETFramework does not
|
||||||
|
// have a built-in facility for dynamically loading user native dlls for the appropriate platform. We therefore
|
||||||
|
// preload the version with the correct architecture (from a corresponding sub-folder relative to us) on static
|
||||||
|
// construction so that subsequent P/Invokes can find it.
|
||||||
|
private static void PreloadWindowsLibrary(string dllFileName)
|
||||||
|
{
|
||||||
|
string basePath = Path.GetDirectoryName(typeof(Interop).Assembly.Location);
|
||||||
|
string architecture = IntPtr.Size == 8 ? "x64" : "x86";
|
||||||
|
string dllPath = Path.Combine(basePath, architecture, dllFileName);
|
||||||
|
|
||||||
|
// return value is intentially ignored as we let the subsequent P/Invokes fail naturally.
|
||||||
|
LoadLibraryExW(dllPath, IntPtr.Zero, LOAD_WITH_ALTERED_SEARCH_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// lpFileName passed to LoadLibraryEx must be a full path.
|
||||||
|
private const int LOAD_WITH_ALTERED_SEARCH_PATH = 0x8;
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
|
||||||
|
private static extern IntPtr LoadLibraryExW(string lpFileName, IntPtr hFile, int dwFlags);
|
||||||
|
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
internal enum hostfxr_resolve_sdk2_flags_t : int
|
||||||
|
{
|
||||||
|
disallow_prerelease = 0x1,
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum hostfxr_resolve_sdk2_result_key_t : int
|
||||||
|
{
|
||||||
|
resolved_sdk_dir = 0,
|
||||||
|
global_json_path = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class Windows
|
||||||
|
{
|
||||||
|
private const CharSet UTF16 = CharSet.Unicode;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = UTF16)]
|
||||||
|
internal delegate void hostfxr_resolve_sdk2_result_fn(
|
||||||
|
hostfxr_resolve_sdk2_result_key_t key,
|
||||||
|
string value);
|
||||||
|
|
||||||
|
[DllImport("hostfxr", CharSet = UTF16, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
internal static extern int hostfxr_resolve_sdk2(
|
||||||
|
string exe_dir,
|
||||||
|
string working_dir,
|
||||||
|
hostfxr_resolve_sdk2_flags_t flags,
|
||||||
|
hostfxr_resolve_sdk2_result_fn result);
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = UTF16)]
|
||||||
|
internal delegate void hostfxr_get_available_sdks_result_fn(
|
||||||
|
int sdk_count,
|
||||||
|
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
|
||||||
|
string[] sdk_dirs);
|
||||||
|
|
||||||
|
[DllImport("hostfxr", CharSet = UTF16, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
internal static extern int hostfxr_get_available_sdks(
|
||||||
|
string exe_dir,
|
||||||
|
hostfxr_get_available_sdks_result_fn result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class Unix
|
||||||
|
{
|
||||||
|
// Ansi marhsaling on Unix is actually UTF8
|
||||||
|
private const CharSet UTF8 = CharSet.Ansi;
|
||||||
|
private static string PtrToStringUTF8(IntPtr ptr) => Marshal.PtrToStringAnsi(ptr);
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = UTF8)]
|
||||||
|
internal delegate void hostfxr_resolve_sdk2_result_fn(
|
||||||
|
hostfxr_resolve_sdk2_result_key_t key,
|
||||||
|
string value);
|
||||||
|
|
||||||
|
[DllImport("hostfxr", CharSet = UTF8, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
internal static extern int hostfxr_resolve_sdk2(
|
||||||
|
string exe_dir,
|
||||||
|
string working_dir,
|
||||||
|
hostfxr_resolve_sdk2_flags_t flags,
|
||||||
|
hostfxr_resolve_sdk2_result_fn result);
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = UTF8)]
|
||||||
|
internal delegate void hostfxr_get_available_sdks_result_fn(
|
||||||
|
int sdk_count,
|
||||||
|
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
|
||||||
|
string[] sdk_dirs);
|
||||||
|
|
||||||
|
[DllImport("hostfxr", CharSet = UTF8, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
internal static extern int hostfxr_get_available_sdks(
|
||||||
|
string exe_dir,
|
||||||
|
hostfxr_get_available_sdks_result_fn result);
|
||||||
|
|
||||||
|
[DllImport("libc", CharSet = UTF8, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern IntPtr realpath(string path, IntPtr buffer);
|
||||||
|
|
||||||
|
[DllImport("libc", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern void free(IntPtr ptr);
|
||||||
|
|
||||||
|
internal static string realpath(string path)
|
||||||
|
{
|
||||||
|
var ptr = realpath(path, IntPtr.Zero);
|
||||||
|
var result = PtrToStringUTF8(ptr);
|
||||||
|
free(ptr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,13 +3,21 @@
|
||||||
|
|
||||||
using Microsoft.Build.Framework;
|
using Microsoft.Build.Framework;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Concurrent;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.MSBuildSdkResolver
|
namespace Microsoft.DotNet.MSBuildSdkResolver
|
||||||
{
|
{
|
||||||
|
// Thread-safety note:
|
||||||
|
// 1. MSBuild can call the same resolver instance in parallel on multiple threads.
|
||||||
|
// 2. Nevertheless, in the IDE, project re-evaluation can create new instances for each evaluation.
|
||||||
|
//
|
||||||
|
// As such, all state (instance or static) must be guarded against concurrent access/updates.
|
||||||
|
// Caches of minimum versions, compatible SDKs are static to benefit multiple IDE evaluations.
|
||||||
|
// VSSettings are also effectively static (singleton instance that can be swapped by tests).
|
||||||
|
|
||||||
public sealed class DotNetMSBuildSdkResolver : SdkResolver
|
public sealed class DotNetMSBuildSdkResolver : SdkResolver
|
||||||
{
|
{
|
||||||
public override string Name => "Microsoft.DotNet.MSBuildSdkResolver";
|
public override string Name => "Microsoft.DotNet.MSBuildSdkResolver";
|
||||||
|
@ -18,29 +26,59 @@ namespace Microsoft.DotNet.MSBuildSdkResolver
|
||||||
public override int Priority => 5000;
|
public override int Priority => 5000;
|
||||||
|
|
||||||
private readonly Func<string, string> _getEnvironmentVariable;
|
private readonly Func<string, string> _getEnvironmentVariable;
|
||||||
|
private readonly VSSettings _vsSettings;
|
||||||
|
|
||||||
|
private static readonly ConcurrentDictionary<string, Version> s_minimumMSBuildVersions
|
||||||
|
= new ConcurrentDictionary<string, Version>();
|
||||||
|
|
||||||
|
private static readonly ConcurrentDictionary<CompatibleSdkKey, CompatibleSdkValue> s_compatibleSdks
|
||||||
|
= new ConcurrentDictionary<CompatibleSdkKey, CompatibleSdkValue>();
|
||||||
|
|
||||||
public DotNetMSBuildSdkResolver()
|
public DotNetMSBuildSdkResolver()
|
||||||
: this(Environment.GetEnvironmentVariable)
|
: this(Environment.GetEnvironmentVariable, VSSettings.Ambient)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test hook to provide environment variables without polluting the test process.
|
// Test constructor
|
||||||
internal DotNetMSBuildSdkResolver(Func<string, string> getEnvironmentVariable)
|
internal DotNetMSBuildSdkResolver(Func<string, string> getEnvironmentVariable, VSSettings vsSettings)
|
||||||
{
|
{
|
||||||
_getEnvironmentVariable = getEnvironmentVariable;
|
_getEnvironmentVariable = getEnvironmentVariable;
|
||||||
|
_vsSettings = vsSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class CachedResult
|
||||||
|
{
|
||||||
|
public string MSBuildSdksDir;
|
||||||
|
public string NETCoreSdkVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override SdkResult Resolve(SdkReference sdkReference, SdkResolverContext context, SdkResultFactory factory)
|
public override SdkResult Resolve(SdkReference sdkReference, SdkResolverContext context, SdkResultFactory factory)
|
||||||
{
|
{
|
||||||
// These are overrides that are used to force the resolved SDK tasks and targets to come from a given
|
string msbuildSdksDir = null;
|
||||||
// base directory and report a given version to msbuild (which may be null if unknown. One key use case
|
string netcoreSdkVersion = null;
|
||||||
// for this is to test SDK tasks and targets without deploying them inside the .NET Core SDK.
|
|
||||||
string msbuildSdksDir = _getEnvironmentVariable("DOTNET_MSBUILD_SDK_RESOLVER_SDKS_DIR");
|
if (context.State is CachedResult priorResult)
|
||||||
string netcoreSdkVersion = _getEnvironmentVariable("DOTNET_MSBUILD_SDK_RESOLVER_SDKS_VER");
|
{
|
||||||
|
msbuildSdksDir = priorResult.MSBuildSdksDir;
|
||||||
|
netcoreSdkVersion = priorResult.NETCoreSdkVersion;
|
||||||
|
}
|
||||||
|
|
||||||
if (msbuildSdksDir == null)
|
if (msbuildSdksDir == null)
|
||||||
{
|
{
|
||||||
string netcoreSdkDir = ResolveNetcoreSdkDirectory(context);
|
// These are overrides that are used to force the resolved SDK tasks and targets to come from a given
|
||||||
|
// base directory and report a given version to msbuild (which may be null if unknown. One key use case
|
||||||
|
// for this is to test SDK tasks and targets without deploying them inside the .NET Core SDK.
|
||||||
|
msbuildSdksDir = _getEnvironmentVariable("DOTNET_MSBUILD_SDK_RESOLVER_SDKS_DIR");
|
||||||
|
netcoreSdkVersion = _getEnvironmentVariable("DOTNET_MSBUILD_SDK_RESOLVER_SDKS_VER");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msbuildSdksDir == null)
|
||||||
|
{
|
||||||
|
string dotnetExeDir = GetDotnetExeDirectory();
|
||||||
|
var resolverResult = ResolveNETCoreSdkDirectory(context, dotnetExeDir);
|
||||||
|
string netcoreSdkDir = resolverResult.ResolvedSdkDirectory;
|
||||||
|
string globalJsonPath = resolverResult.GlobalJsonPath;
|
||||||
|
|
||||||
if (netcoreSdkDir == null)
|
if (netcoreSdkDir == null)
|
||||||
{
|
{
|
||||||
return factory.IndicateFailure(
|
return factory.IndicateFailure(
|
||||||
|
@ -66,21 +104,20 @@ namespace Microsoft.DotNet.MSBuildSdkResolver
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
string minimumMSBuildVersionString = GetMinimumMSBuildVersion(netcoreSdkDir);
|
Version minimumMSBuildVersion = GetMinimumMSBuildVersion(netcoreSdkDir);
|
||||||
var minimumMSBuildVersion = Version.Parse(minimumMSBuildVersionString);
|
|
||||||
if (context.MSBuildVersion < minimumMSBuildVersion)
|
if (context.MSBuildVersion < minimumMSBuildVersion)
|
||||||
{
|
{
|
||||||
return factory.IndicateFailure(
|
return factory.IndicateFailure(
|
||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
$"Version {netcoreSdkVersion} of the .NET Core SDK requires at least version {minimumMSBuildVersionString}"
|
$"Version {netcoreSdkVersion} of the .NET Core SDK requires at least version {minimumMSBuildVersion.ToString()}"
|
||||||
+ $" of MSBuild. The current available version of MSBuild is {context.MSBuildVersion.ToString()}."
|
+ $" of MSBuild. The current available version of MSBuild is {context.MSBuildVersion.ToString()}."
|
||||||
+ " Change the .NET Core SDK specified in global.json to an older version that requires the MSBuild"
|
+ " Change the .NET Core SDK specified in global.json to an older version that requires the MSBuild"
|
||||||
+ " version currently available."
|
+ " version currently available."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
string minimumVSDefinedSDKVersion = GetMinimumVSDefinedSDKVersion();
|
string minimumVSDefinedSDKVersion = GetMinimumVSDefinedSDKVersion();
|
||||||
if (IsNetCoreSDKSmallerThanTheMinimumVersion(netcoreSdkVersion, minimumVSDefinedSDKVersion))
|
if (IsNetCoreSDKSmallerThanTheMinimumVersion(netcoreSdkVersion, minimumVSDefinedSDKVersion))
|
||||||
{
|
{
|
||||||
return factory.IndicateFailure(
|
return factory.IndicateFailure(
|
||||||
|
@ -93,6 +130,12 @@ namespace Microsoft.DotNet.MSBuildSdkResolver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.State = new CachedResult
|
||||||
|
{
|
||||||
|
MSBuildSdksDir = msbuildSdksDir,
|
||||||
|
NETCoreSdkVersion = netcoreSdkVersion
|
||||||
|
};
|
||||||
|
|
||||||
string msbuildSdkDir = Path.Combine(msbuildSdksDir, sdkReference.Name, "Sdk");
|
string msbuildSdkDir = Path.Combine(msbuildSdksDir, sdkReference.Name, "Sdk");
|
||||||
if (!Directory.Exists(msbuildSdkDir))
|
if (!Directory.Exists(msbuildSdkDir))
|
||||||
{
|
{
|
||||||
|
@ -107,18 +150,114 @@ namespace Microsoft.DotNet.MSBuildSdkResolver
|
||||||
return factory.IndicateSuccess(msbuildSdkDir, netcoreSdkVersion);
|
return factory.IndicateSuccess(msbuildSdkDir, netcoreSdkVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetMinimumMSBuildVersion(string netcoreSdkDir)
|
private sealed class CompatibleSdkKey : IEquatable<CompatibleSdkKey>
|
||||||
{
|
{
|
||||||
string minimumVersionFilePath = Path.Combine(netcoreSdkDir, "minimumMSBuildVersion");
|
public readonly string DotnetExeDirectory;
|
||||||
if (!File.Exists(minimumVersionFilePath))
|
public readonly Version MSBuildVersion;
|
||||||
|
|
||||||
|
public CompatibleSdkKey(string dotnetExeDirectory, Version msbuildVersion)
|
||||||
{
|
{
|
||||||
// smallest version that had resolver support and also
|
DotnetExeDirectory = dotnetExeDirectory;
|
||||||
// greater than or equal to the version required by any
|
MSBuildVersion = msbuildVersion;
|
||||||
// .NET Core SDK that did not have this file.
|
|
||||||
return "15.3.0";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return File.ReadLines(minimumVersionFilePath).First().Trim();
|
public bool Equals(CompatibleSdkKey other)
|
||||||
|
{
|
||||||
|
return other != null
|
||||||
|
&& DotnetExeDirectory == other.DotnetExeDirectory
|
||||||
|
&& MSBuildVersion == other.MSBuildVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return Equals(obj as CompatibleSdkValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
int h1 = DotnetExeDirectory.GetHashCode();
|
||||||
|
int h2 = MSBuildVersion.GetHashCode();
|
||||||
|
return ((h1 << 5) + h1) ^ h2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class CompatibleSdkValue
|
||||||
|
{
|
||||||
|
public readonly string MostRecentCompatible;
|
||||||
|
public readonly string MostRecentCompatibleNonPreview;
|
||||||
|
|
||||||
|
public CompatibleSdkValue(string mostRecentCompatible, string mostRecentCompatibleNonPreview)
|
||||||
|
{
|
||||||
|
MostRecentCompatible = mostRecentCompatible;
|
||||||
|
MostRecentCompatibleNonPreview = mostRecentCompatibleNonPreview;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetMostCompatibleSdk(string dotnetExeDirectory, Version msbuildVersion)
|
||||||
|
{
|
||||||
|
CompatibleSdkValue sdks = GetMostCompatibleSdks(dotnetExeDirectory, msbuildVersion);
|
||||||
|
if (_vsSettings.DisallowPrerelease())
|
||||||
|
{
|
||||||
|
return sdks.MostRecentCompatibleNonPreview;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sdks.MostRecentCompatible;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompatibleSdkValue GetMostCompatibleSdks(string dotnetExeDirectory, Version msbuildVersion)
|
||||||
|
{
|
||||||
|
return s_compatibleSdks.GetOrAdd(
|
||||||
|
new CompatibleSdkKey(dotnetExeDirectory, msbuildVersion),
|
||||||
|
key =>
|
||||||
|
{
|
||||||
|
string mostRecent = null;
|
||||||
|
string mostRecentNonPreview = null;
|
||||||
|
|
||||||
|
string[] availableSdks = NETCoreSdkResolver.GetAvailableSdks(key.DotnetExeDirectory);
|
||||||
|
for (int i = availableSdks.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
string netcoreSdkDir = availableSdks[i];
|
||||||
|
string netcoreSdkVersion = new DirectoryInfo(netcoreSdkDir).Name;
|
||||||
|
Version minimumMSBuildVersion = GetMinimumMSBuildVersion(netcoreSdkDir);
|
||||||
|
|
||||||
|
if (key.MSBuildVersion < minimumMSBuildVersion)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mostRecent == null)
|
||||||
|
{
|
||||||
|
mostRecent = netcoreSdkDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (netcoreSdkVersion.IndexOf('-') < 0)
|
||||||
|
{
|
||||||
|
mostRecentNonPreview = netcoreSdkDir;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CompatibleSdkValue(mostRecent, mostRecentNonPreview);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Version GetMinimumMSBuildVersion(string netcoreSdkDir)
|
||||||
|
{
|
||||||
|
return s_minimumMSBuildVersions.GetOrAdd(
|
||||||
|
netcoreSdkDir,
|
||||||
|
dir =>
|
||||||
|
{
|
||||||
|
string minimumVersionFilePath = Path.Combine(netcoreSdkDir, "minimumMSBuildVersion");
|
||||||
|
if (!File.Exists(minimumVersionFilePath))
|
||||||
|
{
|
||||||
|
// smallest version that had resolver support and also
|
||||||
|
// greater than or equal to the version required by any
|
||||||
|
// .NET Core SDK that did not have this file.
|
||||||
|
return new Version(15, 3, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Version.Parse(File.ReadLines(minimumVersionFilePath).First().Trim());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetMinimumVSDefinedSDKVersion()
|
private static string GetMinimumVSDefinedSDKVersion()
|
||||||
|
@ -156,13 +295,24 @@ namespace Microsoft.DotNet.MSBuildSdkResolver
|
||||||
return FXVersion.Compare(netCoreSdkFXVersion, minimumFXVersion) < 0;
|
return FXVersion.Compare(netCoreSdkFXVersion, minimumFXVersion) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ResolveNetcoreSdkDirectory(SdkResolverContext context)
|
private NETCoreSdkResolver.Result ResolveNETCoreSdkDirectory(SdkResolverContext context, string dotnetExeDir)
|
||||||
{
|
{
|
||||||
string exeDir = GetDotnetExeDirectory();
|
string globalJsonStartDir = Path.GetDirectoryName(context.SolutionFilePath ?? context.ProjectFilePath);
|
||||||
string workingDir = context.SolutionFilePath ?? context.ProjectFilePath;
|
var result = NETCoreSdkResolver.ResolveSdk(dotnetExeDir, globalJsonStartDir, _vsSettings.DisallowPrerelease());
|
||||||
string netcoreSdkDir = Interop.hostfxr_resolve_sdk(exeDir, workingDir);
|
|
||||||
|
|
||||||
return netcoreSdkDir;
|
if (result.ResolvedSdkDirectory != null
|
||||||
|
&& result.GlobalJsonPath == null
|
||||||
|
&& context.MSBuildVersion < GetMinimumMSBuildVersion(result.ResolvedSdkDirectory))
|
||||||
|
{
|
||||||
|
string mostCompatible = GetMostCompatibleSdk(dotnetExeDir, context.MSBuildVersion);
|
||||||
|
|
||||||
|
if (mostCompatible != null)
|
||||||
|
{
|
||||||
|
result.ResolvedSdkDirectory = mostCompatible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetDotnetExeDirectory()
|
private string GetDotnetExeDirectory()
|
||||||
|
@ -176,14 +326,12 @@ namespace Microsoft.DotNet.MSBuildSdkResolver
|
||||||
var environmentProvider = new EnvironmentProvider(_getEnvironmentVariable);
|
var environmentProvider = new EnvironmentProvider(_getEnvironmentVariable);
|
||||||
var dotnetExe = environmentProvider.GetCommandPath("dotnet");
|
var dotnetExe = environmentProvider.GetCommandPath("dotnet");
|
||||||
|
|
||||||
#if NETSTANDARD2_0
|
|
||||||
if (dotnetExe != null && !Interop.RunningOnWindows)
|
if (dotnetExe != null && !Interop.RunningOnWindows)
|
||||||
{
|
{
|
||||||
// e.g. on Linux the 'dotnet' command from PATH is a symlink so we need to
|
// e.g. on Linux the 'dotnet' command from PATH is a symlink so we need to
|
||||||
// resolve it to get the actual path to the binary
|
// resolve it to get the actual path to the binary
|
||||||
dotnetExe = Interop.realpath(dotnetExe) ?? dotnetExe;
|
dotnetExe = Interop.Unix.realpath(dotnetExe) ?? dotnetExe;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
return Path.GetDirectoryName(dotnetExe);
|
return Path.GetDirectoryName(dotnetExe);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Build.Framework" Version="$(MicrosoftBuildPackageVersion)" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.Build.Framework" Version="$(MicrosoftBuildPackageVersion)" PrivateAssets="All" />
|
||||||
<PackageReference Include="Microsoft.NETCore.DotNetHostResolver" Version="$(MicrosoftNETCoreDotNetHostResolverPackageVersion)" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.NETCore.DotNetHostResolver" Version="$(MicrosoftNETCoreDotNetHostResolverPackageVersion_ForMSBuildSdkResolverOnly)" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETStandard'">
|
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETStandard'">
|
||||||
|
@ -26,6 +26,7 @@
|
||||||
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">
|
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Setup.Configuration.Interop" Version="1.16.30" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="ResolveHostfxrCopyLocalContent" Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'" DependsOnTargets="RunResolvePackageDependencies" BeforeTargets="AssignTargetPaths">
|
<Target Name="ResolveHostfxrCopyLocalContent" Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'" DependsOnTargets="RunResolvePackageDependencies" BeforeTargets="AssignTargetPaths">
|
||||||
|
@ -40,4 +41,12 @@
|
||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="LinkVSEmbeddableAssemblies" DependsOnTargets="ResolveReferences" AfterTargets="ResolveReferences">
|
||||||
|
<ItemGroup>
|
||||||
|
<ReferencePath Condition="'%(ReferencePath.FileName)' == 'Microsoft.VisualStudio.Setup.Configuration.Interop'">
|
||||||
|
<EmbedInteropTypes>true</EmbedInteropTypes>
|
||||||
|
</ReferencePath>
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -9,14 +9,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.MSBuildSdk
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Tools.Tests.Utilities", "..\..\test\Microsoft.DotNet.Tools.Tests.Utilities\Microsoft.DotNet.Tools.Tests.Utilities.csproj", "{E548D3D0-50E3-4485-B531-95585A5D0B85}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Tools.Tests.Utilities", "..\..\test\Microsoft.DotNet.Tools.Tests.Utilities\Microsoft.DotNet.Tools.Tests.Utilities.csproj", "{E548D3D0-50E3-4485-B531-95585A5D0B85}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.TestFramework", "..\Microsoft.DotNet.TestFramework\Microsoft.DotNet.TestFramework.csproj", "{182FFFA6-AE8F-431C-9B17-2F30B2A8FE42}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestDependencies", "TestDependencies", "{0F45009E-9053-401D-91CA-8046D9EB310B}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestDependencies", "TestDependencies", "{0F45009E-9053-401D-91CA-8046D9EB310B}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.DotNet.Cli.Utils", "..\Microsoft.DotNet.Cli.Utils\Microsoft.DotNet.Cli.Utils.csproj", "{7F68DEFE-F2D3-453C-B155-51B674604D29}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Cli.Utils", "..\Microsoft.DotNet.Cli.Utils\Microsoft.DotNet.Cli.Utils.csproj", "{7F68DEFE-F2D3-453C-B155-51B674604D29}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.InternalAbstractions", "..\Microsoft.DotNet.InternalAbstractions\Microsoft.DotNet.InternalAbstractions.csproj", "{A54567A1-E8DE-4B8C-9156-D895B9D016DB}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.InternalAbstractions", "..\Microsoft.DotNet.InternalAbstractions\Microsoft.DotNet.InternalAbstractions.csproj", "{A54567A1-E8DE-4B8C-9156-D895B9D016DB}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.TestFramework", "..\..\test\Microsoft.DotNet.TestFramework\Microsoft.DotNet.TestFramework.csproj", "{47C99775-27DF-4452-A1A3-2182FFA19BF2}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -35,10 +35,6 @@ Global
|
||||||
{E548D3D0-50E3-4485-B531-95585A5D0B85}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{E548D3D0-50E3-4485-B531-95585A5D0B85}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{E548D3D0-50E3-4485-B531-95585A5D0B85}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{E548D3D0-50E3-4485-B531-95585A5D0B85}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{E548D3D0-50E3-4485-B531-95585A5D0B85}.Release|Any CPU.Build.0 = Release|Any CPU
|
{E548D3D0-50E3-4485-B531-95585A5D0B85}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{182FFFA6-AE8F-431C-9B17-2F30B2A8FE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{182FFFA6-AE8F-431C-9B17-2F30B2A8FE42}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{182FFFA6-AE8F-431C-9B17-2F30B2A8FE42}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{182FFFA6-AE8F-431C-9B17-2F30B2A8FE42}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{7F68DEFE-F2D3-453C-B155-51B674604D29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{7F68DEFE-F2D3-453C-B155-51B674604D29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{7F68DEFE-F2D3-453C-B155-51B674604D29}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{7F68DEFE-F2D3-453C-B155-51B674604D29}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{7F68DEFE-F2D3-453C-B155-51B674604D29}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{7F68DEFE-F2D3-453C-B155-51B674604D29}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
@ -47,14 +43,21 @@ Global
|
||||||
{A54567A1-E8DE-4B8C-9156-D895B9D016DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{A54567A1-E8DE-4B8C-9156-D895B9D016DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{A54567A1-E8DE-4B8C-9156-D895B9D016DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{A54567A1-E8DE-4B8C-9156-D895B9D016DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{A54567A1-E8DE-4B8C-9156-D895B9D016DB}.Release|Any CPU.Build.0 = Release|Any CPU
|
{A54567A1-E8DE-4B8C-9156-D895B9D016DB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{47C99775-27DF-4452-A1A3-2182FFA19BF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{47C99775-27DF-4452-A1A3-2182FFA19BF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{47C99775-27DF-4452-A1A3-2182FFA19BF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{47C99775-27DF-4452-A1A3-2182FFA19BF2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{E548D3D0-50E3-4485-B531-95585A5D0B85} = {0F45009E-9053-401D-91CA-8046D9EB310B}
|
{E548D3D0-50E3-4485-B531-95585A5D0B85} = {0F45009E-9053-401D-91CA-8046D9EB310B}
|
||||||
{182FFFA6-AE8F-431C-9B17-2F30B2A8FE42} = {0F45009E-9053-401D-91CA-8046D9EB310B}
|
|
||||||
{7F68DEFE-F2D3-453C-B155-51B674604D29} = {0F45009E-9053-401D-91CA-8046D9EB310B}
|
{7F68DEFE-F2D3-453C-B155-51B674604D29} = {0F45009E-9053-401D-91CA-8046D9EB310B}
|
||||||
{A54567A1-E8DE-4B8C-9156-D895B9D016DB} = {0F45009E-9053-401D-91CA-8046D9EB310B}
|
{A54567A1-E8DE-4B8C-9156-D895B9D016DB} = {0F45009E-9053-401D-91CA-8046D9EB310B}
|
||||||
|
{47C99775-27DF-4452-A1A3-2182FFA19BF2} = {0F45009E-9053-401D-91CA-8046D9EB310B}
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {DAE5F0E8-9018-4F6B-BB7F-541CA5E0E98B}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.MSBuildSdkResolver
|
||||||
|
{
|
||||||
|
internal static class NETCoreSdkResolver
|
||||||
|
{
|
||||||
|
public sealed class Result
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Path to .NET Core SDK selected by hostfxr (e.g. C:\Program Files\dotnet\sdk\2.1.300).
|
||||||
|
/// </summary>
|
||||||
|
public string ResolvedSdkDirectory;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Path to global.json file that impacted resolution.
|
||||||
|
/// </summary>
|
||||||
|
public string GlobalJsonPath;
|
||||||
|
|
||||||
|
public void Initialize(Interop.hostfxr_resolve_sdk2_result_key_t key, string value)
|
||||||
|
{
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case Interop.hostfxr_resolve_sdk2_result_key_t.resolved_sdk_dir:
|
||||||
|
ResolvedSdkDirectory = value;
|
||||||
|
break;
|
||||||
|
case Interop.hostfxr_resolve_sdk2_result_key_t.global_json_path:
|
||||||
|
GlobalJsonPath = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result ResolveSdk(
|
||||||
|
string dotnetExeDirectory,
|
||||||
|
string globalJsonStartDirectory,
|
||||||
|
bool disallowPrerelease = false)
|
||||||
|
{
|
||||||
|
var result = new Result();
|
||||||
|
var flags = disallowPrerelease ? Interop.hostfxr_resolve_sdk2_flags_t.disallow_prerelease : 0;
|
||||||
|
|
||||||
|
int errorCode = Interop.RunningOnWindows
|
||||||
|
? Interop.Windows.hostfxr_resolve_sdk2(dotnetExeDirectory, globalJsonStartDirectory, flags, result.Initialize)
|
||||||
|
: Interop.Unix.hostfxr_resolve_sdk2(dotnetExeDirectory, globalJsonStartDirectory, flags, result.Initialize);
|
||||||
|
|
||||||
|
Debug.Assert((errorCode == 0) == (result.ResolvedSdkDirectory != null));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class SdkList
|
||||||
|
{
|
||||||
|
public string[] Entries;
|
||||||
|
|
||||||
|
public void Initialize(int count, string[] entries)
|
||||||
|
{
|
||||||
|
entries = entries ?? Array.Empty<string>();
|
||||||
|
Debug.Assert(count == entries.Length);
|
||||||
|
Entries = entries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string[] GetAvailableSdks(string dotnetExeDirectory)
|
||||||
|
{
|
||||||
|
var list = new SdkList();
|
||||||
|
|
||||||
|
int errorCode = Interop.RunningOnWindows
|
||||||
|
? Interop.Windows.hostfxr_get_available_sdks(dotnetExeDirectory, list.Initialize)
|
||||||
|
: Interop.Unix.hostfxr_get_available_sdks(dotnetExeDirectory, list.Initialize);
|
||||||
|
|
||||||
|
return list.Entries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
155
src/Microsoft.DotNet.MSBuildSdkResolver/VSSettings.cs
Normal file
155
src/Microsoft.DotNet.MSBuildSdkResolver/VSSettings.cs
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
#if NET46
|
||||||
|
using Microsoft.VisualStudio.Setup.Configuration;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.MSBuildSdkResolver
|
||||||
|
{
|
||||||
|
internal sealed class VSSettings
|
||||||
|
{
|
||||||
|
private readonly object _lock = new object();
|
||||||
|
private readonly string _settingsFilePath;
|
||||||
|
private readonly bool _disallowPrereleaseByDefault;
|
||||||
|
private FileInfo _settingsFile;
|
||||||
|
private bool _disallowPrerelease;
|
||||||
|
|
||||||
|
// In the product, this singleton is used. It must be safe to use in parallel on multiple threads.
|
||||||
|
// In tests, mock instances can be created with the test constructor below.
|
||||||
|
public static readonly VSSettings Ambient = new VSSettings();
|
||||||
|
|
||||||
|
private VSSettings()
|
||||||
|
{
|
||||||
|
#if NET46
|
||||||
|
if (!Interop.RunningOnWindows)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string instanceId;
|
||||||
|
string installationVersion;
|
||||||
|
bool isPrerelease;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var configuration = new SetupConfiguration();
|
||||||
|
var instance = configuration.GetInstanceForCurrentProcess();
|
||||||
|
|
||||||
|
instanceId = instance.GetInstanceId();
|
||||||
|
installationVersion = instance.GetInstallationVersion();
|
||||||
|
isPrerelease = ((ISetupInstanceCatalog)instance).IsPrerelease();
|
||||||
|
}
|
||||||
|
catch (COMException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var version = Version.Parse(installationVersion);
|
||||||
|
|
||||||
|
_settingsFilePath = Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||||
|
"Microsoft",
|
||||||
|
"VisualStudio",
|
||||||
|
version.Major + ".0_" + instanceId,
|
||||||
|
"sdk.txt");
|
||||||
|
|
||||||
|
_disallowPrereleaseByDefault = !isPrerelease;
|
||||||
|
_disallowPrerelease = _disallowPrereleaseByDefault;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test constructor
|
||||||
|
public VSSettings(string settingsFilePath, bool disallowPrereleaseByDefault)
|
||||||
|
{
|
||||||
|
_settingsFilePath = settingsFilePath;
|
||||||
|
_disallowPrereleaseByDefault = disallowPrereleaseByDefault;
|
||||||
|
_disallowPrerelease = _disallowPrereleaseByDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DisallowPrerelease()
|
||||||
|
{
|
||||||
|
if (_settingsFilePath != null)
|
||||||
|
{
|
||||||
|
Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _disallowPrerelease;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Refresh()
|
||||||
|
{
|
||||||
|
Debug.Assert(_settingsFilePath != null);
|
||||||
|
|
||||||
|
var file = new FileInfo(_settingsFilePath);
|
||||||
|
|
||||||
|
// NB: All calls to Exists and LastWriteTimeUtc below will not hit the disk
|
||||||
|
// They will return data obtained during Refresh() here.
|
||||||
|
file.Refresh();
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
// File does not exist -> use default.
|
||||||
|
if (!file.Exists)
|
||||||
|
{
|
||||||
|
_disallowPrerelease = _disallowPrereleaseByDefault;
|
||||||
|
_settingsFile = file;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// File has not changed -> reuse prior read.
|
||||||
|
if (_settingsFile?.Exists == true && file.LastWriteTimeUtc <= _settingsFile.LastWriteTimeUtc)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// File has changed -> read from disk
|
||||||
|
// If we encounter an I/O exception, assume writer is in the process of updating file,
|
||||||
|
// ignore the exception, and use stale settings until the next resolution.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ReadFromDisk();
|
||||||
|
_settingsFile = file;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (IOException) { }
|
||||||
|
catch (UnauthorizedAccessException) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadFromDisk()
|
||||||
|
{
|
||||||
|
using (var reader = new StreamReader(_settingsFilePath))
|
||||||
|
{
|
||||||
|
string line;
|
||||||
|
while ((line = reader.ReadLine()) != null)
|
||||||
|
{
|
||||||
|
int indexOfEquals = line.IndexOf('=');
|
||||||
|
if (indexOfEquals < 0 || indexOfEquals == (line.Length - 1))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string key = line.Substring(0, indexOfEquals).Trim();
|
||||||
|
string value = line.Substring(indexOfEquals + 1).Trim();
|
||||||
|
|
||||||
|
if (key.Equals("UsePreviews", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& bool.TryParse(value, out bool usePreviews))
|
||||||
|
{
|
||||||
|
_disallowPrerelease = !usePreviews;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// File does not have UsePreviews entry -> use default
|
||||||
|
_disallowPrerelease = _disallowPrereleaseByDefault;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
|
||||||
var resolver = environment.CreateResolver();
|
var resolver = environment.CreateResolver();
|
||||||
var result = (MockResult)resolver.Resolve(
|
var result = (MockResult)resolver.Resolve(
|
||||||
new SdkReference("Some.Test.Sdk", null, null),
|
new SdkReference("Some.Test.Sdk", null, null),
|
||||||
new MockContext { ProjectFilePath = environment.TestDirectory.FullName },
|
new MockContext { ProjectFileDirectory = environment.TestDirectory },
|
||||||
new MockFactory());
|
new MockFactory());
|
||||||
|
|
||||||
result.Success.Should().BeTrue();
|
result.Success.Should().BeTrue();
|
||||||
|
@ -68,7 +68,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
|
||||||
var resolver = environment.CreateResolver();
|
var resolver = environment.CreateResolver();
|
||||||
var result = (MockResult)resolver.Resolve(
|
var result = (MockResult)resolver.Resolve(
|
||||||
new SdkReference("Some.Test.Sdk", null, "999.99.99"),
|
new SdkReference("Some.Test.Sdk", null, "999.99.99"),
|
||||||
new MockContext { ProjectFilePath = environment.TestDirectory.FullName },
|
new MockContext { ProjectFileDirectory = environment.TestDirectory },
|
||||||
new MockFactory());
|
new MockFactory());
|
||||||
|
|
||||||
result.Success.Should().BeFalse();
|
result.Success.Should().BeFalse();
|
||||||
|
@ -81,7 +81,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ItReturnsNullWhenTheSDKRequiresAHigherVersionOfMSBuildThanTheOneAvailable()
|
public void ItReturnsNullWhenTheSDKRequiresAHigherVersionOfMSBuildThanAnyOneAvailable()
|
||||||
{
|
{
|
||||||
var environment = new TestEnvironment();
|
var environment = new TestEnvironment();
|
||||||
var expected =
|
var expected =
|
||||||
|
@ -94,7 +94,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
|
||||||
new MockContext
|
new MockContext
|
||||||
{
|
{
|
||||||
MSBuildVersion = new Version(1, 0),
|
MSBuildVersion = new Version(1, 0),
|
||||||
ProjectFilePath = environment.TestDirectory.FullName
|
ProjectFileDirectory = environment.TestDirectory
|
||||||
},
|
},
|
||||||
new MockFactory());
|
new MockFactory());
|
||||||
|
|
||||||
|
@ -107,6 +107,73 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
|
||||||
+ " version that requires the MSBuild version currently available.");
|
+ " version that requires the MSBuild version currently available.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(true)]
|
||||||
|
[InlineData(false)]
|
||||||
|
public void ItReturnsHighestSdkAvailableThatIsCompatibleWithMSBuild(bool disallowPreviews)
|
||||||
|
{
|
||||||
|
var environment = new TestEnvironment(identifier: disallowPreviews.ToString())
|
||||||
|
{
|
||||||
|
DisallowPrereleaseByDefault = disallowPreviews
|
||||||
|
};
|
||||||
|
|
||||||
|
var compatibleRtm = environment.CreateSdkDirectory(ProgramFiles.X64, "Some.Test.Sdk", "98.98.98", new Version(19, 0, 0, 0));
|
||||||
|
var compatiblePreview = environment.CreateSdkDirectory(ProgramFiles.X64, "Some.Test.Sdk", "99.99.99-preview", new Version(20, 0, 0, 0));
|
||||||
|
var incompatible = environment.CreateSdkDirectory(ProgramFiles.X64, "Some.Test.Sdk", "100.100.100", new Version(21, 0, 0, 0));
|
||||||
|
|
||||||
|
environment.CreateMuxerAndAddToPath(ProgramFiles.X64);
|
||||||
|
|
||||||
|
var resolver = environment.CreateResolver();
|
||||||
|
var result = (MockResult)resolver.Resolve(
|
||||||
|
new SdkReference("Some.Test.Sdk", null, null),
|
||||||
|
new MockContext
|
||||||
|
{
|
||||||
|
MSBuildVersion = new Version(20, 0, 0, 0),
|
||||||
|
ProjectFileDirectory = environment.TestDirectory,
|
||||||
|
},
|
||||||
|
new MockFactory());
|
||||||
|
|
||||||
|
result.Success.Should().BeTrue();
|
||||||
|
result.Path.Should().Be((disallowPreviews ? compatibleRtm : compatiblePreview).FullName);
|
||||||
|
result.Version.Should().Be(disallowPreviews ? "98.98.98" : "99.99.99-preview");
|
||||||
|
result.Warnings.Should().BeNullOrEmpty();
|
||||||
|
result.Errors.Should().BeNullOrEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(true)]
|
||||||
|
[InlineData(false)]
|
||||||
|
public void ItDoesNotReturnHighestSdkAvailableThatIsCompatibleWithMSBuildWhenVersionInGlobalJsonCannotBeFound(bool disallowPreviews)
|
||||||
|
{
|
||||||
|
var environment = new TestEnvironment(callingMethod: "ItDoesNotReturnHighest___", identifier: disallowPreviews.ToString())
|
||||||
|
{
|
||||||
|
DisallowPrereleaseByDefault = disallowPreviews
|
||||||
|
};
|
||||||
|
|
||||||
|
var compatibleRtm = environment.CreateSdkDirectory(ProgramFiles.X64, "Some.Test.Sdk", "98.98.98", new Version(19, 0, 0, 0));
|
||||||
|
var compatiblePreview = environment.CreateSdkDirectory(ProgramFiles.X64, "Some.Test.Sdk", "99.99.99-preview", new Version(20, 0, 0, 0));
|
||||||
|
var incompatible = environment.CreateSdkDirectory(ProgramFiles.X64, "Some.Test.Sdk", "100.100.100", new Version(21, 0, 0, 0));
|
||||||
|
|
||||||
|
environment.CreateMuxerAndAddToPath(ProgramFiles.X64);
|
||||||
|
environment.CreateGlobalJson(environment.TestDirectory, "1.2.3");
|
||||||
|
|
||||||
|
var resolver = environment.CreateResolver();
|
||||||
|
var result = (MockResult)resolver.Resolve(
|
||||||
|
new SdkReference("Some.Test.Sdk", null, null),
|
||||||
|
new MockContext
|
||||||
|
{
|
||||||
|
MSBuildVersion = new Version(20, 0, 0, 0),
|
||||||
|
ProjectFileDirectory = environment.TestDirectory,
|
||||||
|
},
|
||||||
|
new MockFactory());
|
||||||
|
|
||||||
|
result.Success.Should().BeFalse();
|
||||||
|
result.Path.Should().BeNull();
|
||||||
|
result.Version.Should().BeNull();;
|
||||||
|
result.Warnings.Should().BeNullOrEmpty();
|
||||||
|
result.Errors.Should().NotBeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ItReturnsNullWhenTheDefaultVSRequiredSDKVersionIsHigherThanTheSDKVersionAvailable()
|
public void ItReturnsNullWhenTheDefaultVSRequiredSDKVersionIsHigherThanTheSDKVersionAvailable()
|
||||||
{
|
{
|
||||||
|
@ -118,7 +185,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
|
||||||
var resolver = environment.CreateResolver();
|
var resolver = environment.CreateResolver();
|
||||||
var result = (MockResult)resolver.Resolve(
|
var result = (MockResult)resolver.Resolve(
|
||||||
new SdkReference("Some.Test.Sdk", null, "1.0.0"),
|
new SdkReference("Some.Test.Sdk", null, "1.0.0"),
|
||||||
new MockContext { ProjectFilePath = environment.TestDirectory.FullName },
|
new MockContext { ProjectFileDirectory = environment.TestDirectory },
|
||||||
new MockFactory());
|
new MockFactory());
|
||||||
|
|
||||||
result.Success.Should().BeFalse();
|
result.Success.Should().BeFalse();
|
||||||
|
@ -142,7 +209,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
|
||||||
var resolver = environment.CreateResolver();
|
var resolver = environment.CreateResolver();
|
||||||
var result = (MockResult)resolver.Resolve(
|
var result = (MockResult)resolver.Resolve(
|
||||||
new SdkReference("Some.Test.Sdk", null, "1.0.0"),
|
new SdkReference("Some.Test.Sdk", null, "1.0.0"),
|
||||||
new MockContext { ProjectFilePath = environment.TestDirectory.FullName },
|
new MockContext { ProjectFileDirectory = environment.TestDirectory },
|
||||||
new MockFactory());
|
new MockFactory());
|
||||||
|
|
||||||
result.Success.Should().BeFalse();
|
result.Success.Should().BeFalse();
|
||||||
|
@ -165,7 +232,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
|
||||||
var resolver = environment.CreateResolver();
|
var resolver = environment.CreateResolver();
|
||||||
var result = (MockResult)resolver.Resolve(
|
var result = (MockResult)resolver.Resolve(
|
||||||
new SdkReference("Some.Test.Sdk", null, "99.99.99"),
|
new SdkReference("Some.Test.Sdk", null, "99.99.99"),
|
||||||
new MockContext { ProjectFilePath = environment.TestDirectory.FullName },
|
new MockContext { ProjectFileDirectory = environment.TestDirectory },
|
||||||
new MockFactory());
|
new MockFactory());
|
||||||
|
|
||||||
result.Success.Should().BeTrue();
|
result.Success.Should().BeTrue();
|
||||||
|
@ -186,7 +253,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
|
||||||
var resolver = environment.CreateResolver();
|
var resolver = environment.CreateResolver();
|
||||||
var result = (MockResult)resolver.Resolve(
|
var result = (MockResult)resolver.Resolve(
|
||||||
new SdkReference("Some.Test.Sdk", null, "99.99.99"),
|
new SdkReference("Some.Test.Sdk", null, "99.99.99"),
|
||||||
new MockContext { ProjectFilePath = environment.TestDirectory.FullName },
|
new MockContext { ProjectFileDirectory = environment.TestDirectory },
|
||||||
new MockFactory());
|
new MockFactory());
|
||||||
|
|
||||||
result.Success.Should().BeTrue();
|
result.Success.Should().BeTrue();
|
||||||
|
@ -196,6 +263,159 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
|
||||||
result.Errors.Should().BeNullOrEmpty();
|
result.Errors.Should().BeNullOrEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(true)]
|
||||||
|
[InlineData(false)]
|
||||||
|
public void ItDisallowsPreviewsBasedOnDefault(bool disallowPreviewsByDefault)
|
||||||
|
{
|
||||||
|
var environment = new TestEnvironment(identifier: disallowPreviewsByDefault.ToString());
|
||||||
|
var rtm = environment.CreateSdkDirectory(ProgramFiles.X64, "Some.Test.Sdk", "10.0.0");
|
||||||
|
var preview = environment.CreateSdkDirectory(ProgramFiles.X64, "Some.Test.Sdk", "11.0.0-preview1");
|
||||||
|
var expected = disallowPreviewsByDefault ? rtm : preview;
|
||||||
|
|
||||||
|
environment.CreateMuxerAndAddToPath(ProgramFiles.X64);
|
||||||
|
environment.DisallowPrereleaseByDefault = disallowPreviewsByDefault;
|
||||||
|
|
||||||
|
var resolver = environment.CreateResolver();
|
||||||
|
var result = (MockResult)resolver.Resolve(
|
||||||
|
new SdkReference("Some.Test.Sdk", null, null),
|
||||||
|
new MockContext { ProjectFileDirectory = environment.TestDirectory },
|
||||||
|
new MockFactory());
|
||||||
|
|
||||||
|
result.Success.Should().BeTrue();
|
||||||
|
result.Path.Should().Be(expected.FullName);
|
||||||
|
result.Version.Should().Be(disallowPreviewsByDefault ? "10.0.0" : "11.0.0-preview1");
|
||||||
|
result.Warnings.Should().BeNullOrEmpty();
|
||||||
|
result.Errors.Should().BeNullOrEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(true)]
|
||||||
|
[InlineData(false)]
|
||||||
|
public void ItDisallowsPreviewsBasedOnFile(bool disallowPreviews)
|
||||||
|
{
|
||||||
|
var environment = new TestEnvironment(identifier: disallowPreviews.ToString());
|
||||||
|
var rtm = environment.CreateSdkDirectory(ProgramFiles.X64, "Some.Test.Sdk", "10.0.0");
|
||||||
|
var preview = environment.CreateSdkDirectory(ProgramFiles.X64, "Some.Test.Sdk", "11.0.0-preview1");
|
||||||
|
var expected = disallowPreviews ? rtm : preview;
|
||||||
|
|
||||||
|
environment.CreateMuxerAndAddToPath(ProgramFiles.X64);
|
||||||
|
environment.DisallowPrereleaseByDefault = !disallowPreviews;
|
||||||
|
environment.CreateVSSettingsFile(disallowPreviews);
|
||||||
|
|
||||||
|
var resolver = environment.CreateResolver();
|
||||||
|
var result = (MockResult)resolver.Resolve(
|
||||||
|
new SdkReference("Some.Test.Sdk", null, null),
|
||||||
|
new MockContext { ProjectFileDirectory = environment.TestDirectory },
|
||||||
|
new MockFactory());
|
||||||
|
|
||||||
|
result.Success.Should().BeTrue();
|
||||||
|
result.Path.Should().Be(expected.FullName);
|
||||||
|
result.Version.Should().Be(disallowPreviews ? "10.0.0" : "11.0.0-preview1");
|
||||||
|
result.Warnings.Should().BeNullOrEmpty();
|
||||||
|
result.Errors.Should().BeNullOrEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ItObservesChangesToVSSettingsFile()
|
||||||
|
{
|
||||||
|
var environment = new TestEnvironment();
|
||||||
|
var rtm = environment.CreateSdkDirectory(ProgramFiles.X64, "Some.Test.Sdk", "10.0.0");
|
||||||
|
var preview = environment.CreateSdkDirectory(ProgramFiles.X64, "Some.Test.Sdk", "11.0.0-preview1");
|
||||||
|
|
||||||
|
environment.CreateMuxerAndAddToPath(ProgramFiles.X64);
|
||||||
|
environment.CreateVSSettingsFile(disallowPreviews: true);
|
||||||
|
var resolver = environment.CreateResolver();
|
||||||
|
|
||||||
|
void Check(bool disallowPreviews, string message)
|
||||||
|
{
|
||||||
|
// check twice because file-up-to-date is a separate code path
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
var result = (MockResult)resolver.Resolve(
|
||||||
|
new SdkReference("Some.Test.Sdk", null, null),
|
||||||
|
new MockContext { ProjectFileDirectory = environment.TestDirectory },
|
||||||
|
new MockFactory());
|
||||||
|
|
||||||
|
string m = $"{message} ({i})";
|
||||||
|
var expected = disallowPreviews ? rtm : preview;
|
||||||
|
result.Success.Should().BeTrue(m);
|
||||||
|
result.Path.Should().Be(expected.FullName, m);
|
||||||
|
result.Version.Should().Be(disallowPreviews ? "10.0.0" : "11.0.0-preview1", m);
|
||||||
|
result.Warnings.Should().BeNullOrEmpty(m);
|
||||||
|
result.Errors.Should().BeNullOrEmpty(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
environment.DeleteVSSettingsFile();
|
||||||
|
Check(disallowPreviews: false, message: "default with no file");
|
||||||
|
|
||||||
|
environment.CreateVSSettingsFile(disallowPreviews: true);
|
||||||
|
Check(disallowPreviews: true, message: "file changed to disallow previews");
|
||||||
|
|
||||||
|
environment.CreateVSSettingsFile(disallowPreviews: false);
|
||||||
|
Check(disallowPreviews: false, message: "file changed to not disallow previews");
|
||||||
|
|
||||||
|
environment.CreateVSSettingsFile(disallowPreviews: true);
|
||||||
|
Check(disallowPreviews: true, message: "file changed back to disallow previews");
|
||||||
|
|
||||||
|
environment.DeleteVSSettingsFile();
|
||||||
|
Check(disallowPreviews: false, message: "file deleted to return to default");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ItAllowsPreviewWhenGlobalJsonHasPreviewIrrespectiveOfSetting()
|
||||||
|
{
|
||||||
|
var environment = new TestEnvironment();
|
||||||
|
var rtm = environment.CreateSdkDirectory(ProgramFiles.X64, "Some.Test.Sdk", "10.0.0");
|
||||||
|
var preview = environment.CreateSdkDirectory(ProgramFiles.X64, "Some.Test.Sdk", "11.0.0-preview1");
|
||||||
|
|
||||||
|
environment.CreateMuxerAndAddToPath(ProgramFiles.X64);
|
||||||
|
environment.DisallowPrereleaseByDefault = true;
|
||||||
|
environment.CreateGlobalJson(environment.TestDirectory, "11.0.0-preview1");
|
||||||
|
|
||||||
|
var resolver = environment.CreateResolver();
|
||||||
|
var result = (MockResult)resolver.Resolve(
|
||||||
|
new SdkReference("Some.Test.Sdk", null, null),
|
||||||
|
new MockContext { ProjectFileDirectory = environment.TestDirectory },
|
||||||
|
new MockFactory());
|
||||||
|
|
||||||
|
result.Success.Should().BeTrue();
|
||||||
|
result.Path.Should().Be(preview.FullName);
|
||||||
|
result.Version.Should().Be("11.0.0-preview1");
|
||||||
|
result.Warnings.Should().BeNullOrEmpty();
|
||||||
|
result.Errors.Should().BeNullOrEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ItRespectsAmbientVSSettings()
|
||||||
|
{
|
||||||
|
// When run in test explorer in VS, this will actually locate the settings for the current VS instance
|
||||||
|
// based on location of testhost executable. This gives us some coverage threw that path but we cannot
|
||||||
|
// fix our expectations since the behavior will vary (by design) based on the current VS instance's settings.
|
||||||
|
var vsSettings = VSSettings.Ambient;
|
||||||
|
|
||||||
|
var environment = new TestEnvironment();
|
||||||
|
var rtm = environment.CreateSdkDirectory(ProgramFiles.X64, "Some.Test.Sdk", "10.0.0");
|
||||||
|
var preview = environment.CreateSdkDirectory(ProgramFiles.X64, "Some.Test.Sdk", "11.0.0-preview1");
|
||||||
|
var expected = vsSettings.DisallowPrerelease() ? rtm : preview;
|
||||||
|
|
||||||
|
environment.CreateMuxerAndAddToPath(ProgramFiles.X64);
|
||||||
|
|
||||||
|
var resolver = environment.CreateResolver(useAmbientSettings: true);
|
||||||
|
var result = (MockResult)resolver.Resolve(
|
||||||
|
new SdkReference("Some.Test.Sdk", null, null),
|
||||||
|
new MockContext { ProjectFileDirectory = environment.TestDirectory },
|
||||||
|
new MockFactory());
|
||||||
|
|
||||||
|
result.Success.Should().BeTrue();
|
||||||
|
result.Path.Should().Be(expected.FullName);
|
||||||
|
result.Version.Should().Be(vsSettings.DisallowPrerelease() ? "10.0.0" : "11.0.0-preview1");
|
||||||
|
result.Warnings.Should().BeNullOrEmpty();
|
||||||
|
result.Errors.Should().BeNullOrEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
private enum ProgramFiles
|
private enum ProgramFiles
|
||||||
{
|
{
|
||||||
X64,
|
X64,
|
||||||
|
@ -210,6 +430,8 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
|
||||||
public string PathEnvironmentVariable { get; set; }
|
public string PathEnvironmentVariable { get; set; }
|
||||||
|
|
||||||
public DirectoryInfo TestDirectory { get; }
|
public DirectoryInfo TestDirectory { get; }
|
||||||
|
public FileInfo VSSettingsFile { get; set; }
|
||||||
|
public bool DisallowPrereleaseByDefault { get; set; }
|
||||||
|
|
||||||
public TestEnvironment(string identifier = "", [CallerMemberName] string callingMethod = "")
|
public TestEnvironment(string identifier = "", [CallerMemberName] string callingMethod = "")
|
||||||
{
|
{
|
||||||
|
@ -223,8 +445,12 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
|
||||||
PathEnvironmentVariable = string.Empty;
|
PathEnvironmentVariable = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SdkResolver CreateResolver()
|
public SdkResolver CreateResolver(bool useAmbientSettings = false)
|
||||||
=> new DotNetMSBuildSdkResolver(GetEnvironmentVariable);
|
=> new DotNetMSBuildSdkResolver(
|
||||||
|
GetEnvironmentVariable,
|
||||||
|
useAmbientSettings
|
||||||
|
? VSSettings.Ambient
|
||||||
|
: new VSSettings(VSSettingsFile?.FullName, DisallowPrereleaseByDefault));
|
||||||
|
|
||||||
public DirectoryInfo GetSdkDirectory(ProgramFiles programFiles, string sdkName, string sdkVersion)
|
public DirectoryInfo GetSdkDirectory(ProgramFiles programFiles, string sdkName, string sdkVersion)
|
||||||
=> TestDirectory.GetDirectory(
|
=> TestDirectory.GetDirectory(
|
||||||
|
@ -317,6 +543,34 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
|
||||||
string baseDirectory = AppContext.BaseDirectory;
|
string baseDirectory = AppContext.BaseDirectory;
|
||||||
return Path.Combine(baseDirectory, "minimumVSDefinedSDKVersion");
|
return Path.Combine(baseDirectory, "minimumVSDefinedSDKVersion");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void CreateVSSettingsFile(bool disallowPreviews)
|
||||||
|
{
|
||||||
|
VSSettingsFile = TestDirectory.GetFile("sdk.txt");
|
||||||
|
|
||||||
|
// Guard against tests writing too fast for the up-to-date check
|
||||||
|
// It happens more often on Unix due to https://github.com/dotnet/corefx/issues/12403
|
||||||
|
var lastWriteTimeUtc = VSSettingsFile.Exists ? VSSettingsFile.LastWriteTimeUtc : DateTime.MinValue;
|
||||||
|
for (int sleep = 10; sleep < 3000; sleep *= 2)
|
||||||
|
{
|
||||||
|
File.WriteAllText(VSSettingsFile.FullName, $"UsePreviews={!disallowPreviews}");
|
||||||
|
VSSettingsFile.Refresh();
|
||||||
|
|
||||||
|
if (VSSettingsFile.LastWriteTimeUtc > lastWriteTimeUtc)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Threading.Thread.Sleep(sleep);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException("LastWriteTime is not changing.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteVSSettingsFile()
|
||||||
|
{
|
||||||
|
VSSettingsFile.Delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class MockContext : SdkResolverContext
|
private sealed class MockContext : SdkResolverContext
|
||||||
|
@ -325,6 +579,12 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
|
||||||
public new string SolutionFilePath { get => base.SolutionFilePath; set => base.SolutionFilePath = value; }
|
public new string SolutionFilePath { get => base.SolutionFilePath; set => base.SolutionFilePath = value; }
|
||||||
public new Version MSBuildVersion { get => base.MSBuildVersion; set => base.MSBuildVersion = value; }
|
public new Version MSBuildVersion { get => base.MSBuildVersion; set => base.MSBuildVersion = value; }
|
||||||
|
|
||||||
|
public DirectoryInfo ProjectFileDirectory
|
||||||
|
{
|
||||||
|
get => new DirectoryInfo(Path.GetDirectoryName(ProjectFilePath));
|
||||||
|
set => ProjectFilePath = value.GetFile("test.csproj").FullName;
|
||||||
|
}
|
||||||
|
|
||||||
public MockContext()
|
public MockContext()
|
||||||
{
|
{
|
||||||
MSBuildVersion = new Version(15, 3, 0);
|
MSBuildVersion = new Version(15, 3, 0);
|
||||||
|
@ -352,12 +612,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
|
||||||
Errors = errors;
|
Errors = errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public new bool Success
|
public override bool Success { get; protected set; }
|
||||||
{
|
|
||||||
get => base.Success;
|
|
||||||
private set => base.Success = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string Version { get; protected set; }
|
public override string Version { get; protected set; }
|
||||||
public override string Path { get; protected set; }
|
public override string Path { get; protected set; }
|
||||||
public IEnumerable<string> Errors { get; }
|
public IEnumerable<string> Errors { get; }
|
||||||
|
|
|
@ -2,6 +2,14 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net461;$(CliTargetFramework)</TargetFrameworks>
|
<TargetFrameworks>net461;$(CliTargetFramework)</TargetFrameworks>
|
||||||
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">$(CliTargetFramework)</TargetFrameworks>
|
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">$(CliTargetFramework)</TargetFrameworks>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
https://github.com/dotnet/cli/issues/9851: For now test only on net461 on Windows because
|
||||||
|
we can't load a different hostfxr.dll into the test than the one that is already loaded
|
||||||
|
on Core.
|
||||||
|
-->
|
||||||
|
<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">net461</TargetFrameworks>
|
||||||
|
|
||||||
<RuntimeFrameworkVersion>$(MicrosoftNETCoreAppPackageVersion)</RuntimeFrameworkVersion>
|
<RuntimeFrameworkVersion>$(MicrosoftNETCoreAppPackageVersion)</RuntimeFrameworkVersion>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<AssemblyOriginatorKeyFile>../../tools/Key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>../../tools/Key.snk</AssemblyOriginatorKeyFile>
|
||||||
|
@ -19,9 +27,9 @@
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
|
||||||
<PackageReference Include="xunit" Version="2.2.0" />
|
<PackageReference Include="xunit" Version="2.2.0" />
|
||||||
<PackageReference Include="runtime.linux-x64.Microsoft.NETCore.DotNetHostResolver" Version="$(MicrosoftNETCoreDotNetHostResolverPackageVersion)" />
|
<PackageReference Include="runtime.linux-x64.Microsoft.NETCore.DotNetHostResolver" Version="$(MicrosoftNETCoreDotNetHostResolverPackageVersion_ForMSBuildSdkResolverOnly)" />
|
||||||
<PackageReference Include="runtime.osx-x64.Microsoft.NETCore.DotNetHostResolver" Version="$(MicrosoftNETCoreDotNetHostResolverPackageVersion)" />
|
<PackageReference Include="runtime.osx-x64.Microsoft.NETCore.DotNetHostResolver" Version="$(MicrosoftNETCoreDotNetHostResolverPackageVersion_ForMSBuildSdkResolverOnly)" />
|
||||||
<PackageReference Include="runtime.linux-musl-x64.Microsoft.NETCore.DotNetHostResolver" Version="$(MicrosoftNETCoreDotNetHostResolverPackageVersion)" />
|
<PackageReference Include="runtime.linux-musl-x64.Microsoft.NETCore.DotNetHostResolver" Version="$(MicrosoftNETCoreDotNetHostResolverPackageVersion_ForMSBuildSdkResolverOnly)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -32,7 +40,10 @@
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<!--
|
||||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
https://github.com/dotnet/cli/issues/9851: We also can't load a different hostfxr.dll on RHEL
|
||||||
|
-->
|
||||||
|
<ItemGroup Condition="$(Rid.StartsWith('rhel'))">
|
||||||
|
<Compile Remove="GivenAnMSBuildSdkResolver.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -152,11 +152,14 @@ namespace Microsoft.DotNet.Cli.Test.Tests
|
||||||
.ExecuteWithCapturedOutput("--logger \"trx;logfilename=custom.trx\" --logger console;verbosity=normal -- RunConfiguration.ResultsDirectory=" + trxLoggerDirectory);
|
.ExecuteWithCapturedOutput("--logger \"trx;logfilename=custom.trx\" --logger console;verbosity=normal -- RunConfiguration.ResultsDirectory=" + trxLoggerDirectory);
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
var trxFilePath = Path.Combine(trxLoggerDirectory, "custom.trx");
|
if (!DotnetUnderTest.IsLocalized())
|
||||||
Assert.True(File.Exists(trxFilePath));
|
{
|
||||||
result.StdOut.Should().Contain(trxFilePath);
|
var trxFilePath = Path.Combine(trxLoggerDirectory, "custom.trx");
|
||||||
result.StdOut.Should().Contain("Passed VSTestPassTest");
|
Assert.True(File.Exists(trxFilePath));
|
||||||
result.StdOut.Should().Contain("Failed VSTestFailTest");
|
result.StdOut.Should().Contain(trxFilePath);
|
||||||
|
result.StdOut.Should().Contain("Passed VSTestPassTest");
|
||||||
|
result.StdOut.Should().Contain("Failed VSTestFailTest");
|
||||||
|
}
|
||||||
|
|
||||||
// Cleanup trxLoggerDirectory if it exist
|
// Cleanup trxLoggerDirectory if it exist
|
||||||
if (Directory.Exists(trxLoggerDirectory))
|
if (Directory.Exists(trxLoggerDirectory))
|
||||||
|
|
Loading…
Reference in a new issue