From d26b41a0c3aa9f0217a3722bc5dca5147d6d6af8 Mon Sep 17 00:00:00 2001 From: Nick Guerrera Date: Wed, 26 Apr 2017 15:01:59 -0700 Subject: [PATCH 1/5] Add msbuild sdk resolver --- build/Compile.targets | 2 + build/DependencyVersions.props | 2 +- .../Microsoft.DotNet.Cli.Utils.csproj | 5 +- .../Interop.Common.cs | 36 +++++ .../Interop.NETFramework.cs | 45 +++++++ .../Interop.NETStandard.cs | 37 ++++++ .../MSBuildSdkResolver.cs | 123 ++++++++++++++++++ ...Microsoft.DotNet.MSBuildSdkResolver.csproj | 38 ++++++ .../Microsoft.DotNet.MSBuildSdkResolver.sln | 28 ++++ test/Microsoft.DotNet.Cli.Tests.sln | 14 ++ .../GivenAnMSBuildSdkResolver.cs | 70 ++++++++++ ...oft.DotNet.MSBuildSdkResolver.Tests.csproj | 29 +++++ .../xunit.runner.json | 3 + 13 files changed, 429 insertions(+), 3 deletions(-) create mode 100644 src/Microsoft.DotNet.MSBuildSdkResolver/Interop.Common.cs create mode 100644 src/Microsoft.DotNet.MSBuildSdkResolver/Interop.NETFramework.cs create mode 100644 src/Microsoft.DotNet.MSBuildSdkResolver/Interop.NETStandard.cs create mode 100644 src/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs create mode 100644 src/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj create mode 100644 src/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.sln create mode 100644 test/Microsoft.DotNet.MSBuildSdkResolver.Tests/GivenAnMSBuildSdkResolver.cs create mode 100644 test/Microsoft.DotNet.MSBuildSdkResolver.Tests/Microsoft.DotNet.MSBuildSdkResolver.Tests.csproj create mode 100644 test/Microsoft.DotNet.MSBuildSdkResolver.Tests/xunit.runner.json diff --git a/build/Compile.targets b/build/Compile.targets index b2ebb2ebc..b44ed8380 100644 --- a/build/Compile.targets +++ b/build/Compile.targets @@ -12,5 +12,7 @@ + + diff --git a/build/DependencyVersions.props b/build/DependencyVersions.props index 64284b4d5..e025ef04a 100644 --- a/build/DependencyVersions.props +++ b/build/DependencyVersions.props @@ -2,7 +2,7 @@ 2.0.0-preview1-002101-00 - 15.2.0-preview-000093-02 + 15.3.0-preview-000111-01 2.0.0-rc4-61325-08 2.0.0-alpha-20170428-1 4.3.0-beta1-2418 diff --git a/src/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj b/src/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj index 5d14b7b57..bc95703fb 100644 --- a/src/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj +++ b/src/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj @@ -20,7 +20,8 @@ - + + @@ -31,4 +32,4 @@ - \ No newline at end of file + diff --git a/src/Microsoft.DotNet.MSBuildSdkResolver/Interop.Common.cs b/src/Microsoft.DotNet.MSBuildSdkResolver/Interop.Common.cs new file mode 100644 index 000000000..580d1220b --- /dev/null +++ b/src/Microsoft.DotNet.MSBuildSdkResolver/Interop.Common.cs @@ -0,0 +1,36 @@ +// 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(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.MSBuildSdkResolver/Interop.NETFramework.cs b/src/Microsoft.DotNet.MSBuildSdkResolver/Interop.NETFramework.cs new file mode 100644 index 000000000..d1d567e8b --- /dev/null +++ b/src/Microsoft.DotNet.MSBuildSdkResolver/Interop.NETFramework.cs @@ -0,0 +1,45 @@ +// 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 + { + 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 \ No newline at end of file diff --git a/src/Microsoft.DotNet.MSBuildSdkResolver/Interop.NETStandard.cs b/src/Microsoft.DotNet.MSBuildSdkResolver/Interop.NETStandard.cs new file mode 100644 index 000000000..bff6fd84b --- /dev/null +++ b/src/Microsoft.DotNet.MSBuildSdkResolver/Interop.NETStandard.cs @@ -0,0 +1,37 @@ +// 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: Currently, only the NET46 build ships (with Visual Studio/desktop msbuild), +// but the netstandard1.3 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. +#if NETSTANDARD1_3 + +using System.Runtime.InteropServices; +using System.Text; + +namespace Microsoft.DotNet.MSBuildSdkResolver +{ + internal static partial class Interop + { + internal static readonly bool s_runningOnWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + 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 s_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); + } +} + +#endif // NETSTANDARD1_3 \ No newline at end of file diff --git a/src/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs b/src/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs new file mode 100644 index 000000000..fefbd46d0 --- /dev/null +++ b/src/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs @@ -0,0 +1,123 @@ +// 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 Microsoft.Build.Framework; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Microsoft.DotNet.MSBuildSdkResolver +{ + public sealed class DotNetMSBuildSdkResolver : SdkResolver + { + public override string Name => "Microsoft.DotNet.MSBuildSdkResolver"; + + // Default resolver has priority 10000 and we want to go before it and leave room on either side of us. + public override int Priority => 5000; + + 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 + // 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. + string msbuildSdksDir = Environment.GetEnvironmentVariable("DOTNET_MSBUILD_SDK_RESOLVER_SDKS_DIR"); + string netcoreSdkVersion = Environment.GetEnvironmentVariable("DOTNET_MSBUILD_SDK_RESOLVER_SDKS_VER"); + + if (msbuildSdksDir == null) + { + string netcoreSdkDir = ResolveNetcoreSdkDirectory(context); + if (netcoreSdkDir == null) + { + return factory.IndicateFailure( + new[] + { + "Unable to locate the .NET Core SDK. Check that it is installed and that the version" + + "specified in global.json (if any) matches the installed version." + }); + } + + msbuildSdksDir = Path.Combine(netcoreSdkDir, "Sdks"); + netcoreSdkVersion = new DirectoryInfo(netcoreSdkDir).Name;; + } + + string msbuildSdkDir = Path.Combine(msbuildSdksDir, sdkReference.Name, "Sdk"); + if (!Directory.Exists(msbuildSdkDir)) + { + return factory.IndicateFailure( + new[] + { + $"{msbuildSdkDir} not found. Check that a recent enough .NET Core SDK is installed" + + " and/or increase the version specified in global.json. " + }); + } + + return factory.IndicateSuccess(msbuildSdkDir, netcoreSdkVersion); + } + + private string ResolveNetcoreSdkDirectory(SdkResolverContext context) + { + foreach (string exeDir in GetDotnetExeDirectoryCandidates()) + { + string workingDir = context.SolutionFilePath ?? context.ProjectFilePath; + string netcoreSdkDir = Interop.hostfxr_resolve_sdk(exeDir, workingDir); + + if (netcoreSdkDir != null) + { + return netcoreSdkDir; + } + } + + return null; + } + + // Search for [ProgramFiles]\dotnet in this order. Only ProgramFiles is defined on + private static readonly string[] s_programFiles = new[] + { + // "c:\Program Files" on x64 machine regardless process bitness, undefined on x86 machines. + "ProgramW6432", + + // "c:\Program Files (x86)" on x64 machine regardless of process bitness, undefined on x64 machines. + "ProgramFiles(x86)", + + // "c:\Program Files" in x64 process, "c:\Program Files (x86)" in x86 process. + // hostfxr will search this on its own if multilevel lookup is not disable, but + // we do it explicitly to prevent an environment with disabled multilevel lookup + // from crippling desktop msbuild and VS. + "ProgramFiles", + }; + + private List GetDotnetExeDirectoryCandidates() + { + string environmentOverride = Environment.GetEnvironmentVariable("DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR"); + if (environmentOverride != null) + { + return new List(1) { environmentOverride }; + } + + // Initial capacity is 2 because while there are 3 candidates, we expect at most 2 unique ones (x64 + x86) + // Also, N=3 here means that we needn't be concerned with the O(N^2) complexity of the foreach + contains. + var candidates = new List(2); + foreach (string variable in s_programFiles) + { + string directory = Environment.GetEnvironmentVariable(variable); + if (directory == null) + { + continue; + } + + directory = Path.Combine(directory, "dotnet"); + if (!candidates.Contains(directory)) + { + candidates.Add(directory); + } + } + + if (candidates.Count == 0) + { + candidates.Add(null); + } + + return candidates; + } + } +} diff --git a/src/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj b/src/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj new file mode 100644 index 000000000..b6399659d --- /dev/null +++ b/src/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj @@ -0,0 +1,38 @@ + + + + + $(SdkVersion) + netstandard1.3;net46 + netstandard1.3 + AnyCPU + win-x86;win-x64 + true + ../../tools/Key.snk + true + true + false + + + + + + + + + + + + + + + x86/hostfxr.dll + PreserveNewest + + + x64/hostfxr.dll + PreserveNewest + + + + \ No newline at end of file diff --git a/src/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.sln b/src/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.sln new file mode 100644 index 000000000..4d5062122 --- /dev/null +++ b/src/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26425.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.MSBuildSdkResolver", "Microsoft.DotNet.MSBuildSdkResolver.csproj", "{DCB2A518-7BC6-43F5-BE2C-13B11A1F3961}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.MSBuildSdkResolver.Tests", "..\..\test\Microsoft.DotNet.MSBuildSdkResolver.Tests\Microsoft.DotNet.MSBuildSdkResolver.Tests.csproj", "{CC488F39-E106-4BF4-9599-19A265AFD9AC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DCB2A518-7BC6-43F5-BE2C-13B11A1F3961}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DCB2A518-7BC6-43F5-BE2C-13B11A1F3961}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DCB2A518-7BC6-43F5-BE2C-13B11A1F3961}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DCB2A518-7BC6-43F5-BE2C-13B11A1F3961}.Release|Any CPU.Build.0 = Release|Any CPU + {CC488F39-E106-4BF4-9599-19A265AFD9AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC488F39-E106-4BF4-9599-19A265AFD9AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC488F39-E106-4BF4-9599-19A265AFD9AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC488F39-E106-4BF4-9599-19A265AFD9AC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/Microsoft.DotNet.Cli.Tests.sln b/test/Microsoft.DotNet.Cli.Tests.sln index 30e4e2f60..ffc08cbd5 100644 --- a/test/Microsoft.DotNet.Cli.Tests.sln +++ b/test/Microsoft.DotNet.Cli.Tests.sln @@ -76,6 +76,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-store.Tests", "dotne EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-back-compat.Tests", "dotnet-back-compat.Tests\dotnet-back-compat.Tests.csproj", "{27351B2F-325B-4843-9F4C-BC53FD06A7B5}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.MSBuildSdkResolver.Tests", "Microsoft.DotNet.MSBuildSdkResolver.Tests\Microsoft.DotNet.MSBuildSdkResolver.Tests.csproj", "{42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -506,6 +508,18 @@ Global {27351B2F-325B-4843-9F4C-BC53FD06A7B5}.Release|x64.Build.0 = Release|Any CPU {27351B2F-325B-4843-9F4C-BC53FD06A7B5}.Release|x86.ActiveCfg = Release|Any CPU {27351B2F-325B-4843-9F4C-BC53FD06A7B5}.Release|x86.Build.0 = Release|Any CPU + {42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}.Debug|x64.ActiveCfg = Debug|Any CPU + {42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}.Debug|x64.Build.0 = Debug|Any CPU + {42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}.Debug|x86.ActiveCfg = Debug|Any CPU + {42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}.Debug|x86.Build.0 = Debug|Any CPU + {42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}.Release|Any CPU.Build.0 = Release|Any CPU + {42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}.Release|x64.ActiveCfg = Release|Any CPU + {42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}.Release|x64.Build.0 = Release|Any CPU + {42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}.Release|x86.ActiveCfg = Release|Any CPU + {42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/test/Microsoft.DotNet.MSBuildSdkResolver.Tests/GivenAnMSBuildSdkResolver.cs b/test/Microsoft.DotNet.MSBuildSdkResolver.Tests/GivenAnMSBuildSdkResolver.cs new file mode 100644 index 000000000..bfbf14e4a --- /dev/null +++ b/test/Microsoft.DotNet.MSBuildSdkResolver.Tests/GivenAnMSBuildSdkResolver.cs @@ -0,0 +1,70 @@ +// 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.Collections.Generic; +using Microsoft.Build.Framework; +using Xunit; +using System.Linq; +using Xunit.Abstractions; +using System; +using Microsoft.DotNet.MSBuildSdkResolver; + +namespace Microsoft.DotNet.Cli.Utils.Tests +{ + public class GivenAnMSBuildSdkResolver + { + private ITestOutputHelper _logger; + + public GivenAnMSBuildSdkResolver(ITestOutputHelper logger) + { + _logger = logger; + } + + [Fact] + public void ItHasCorrectNameAndPriority() + { + var resolver = new DotNetMSBuildSdkResolver(); + + Assert.Equal(5000, resolver.Priority); + Assert.Equal("Microsoft.DotNet.MSBuildSdkResolver", resolver.Name); + } + + [Fact] + public void ItCallsNativeCodeWithoutCrashing() // WIP: placeholder to get plumbing through + { + var resolver = new DotNetMSBuildSdkResolver(); + var result = (MockResult)resolver.Resolve( + new SdkReference("Microsoft.NET.Sdk", null, null), + new MockContext(), + new MockFactory()); + + _logger.WriteLine($"success: {result.Success}"); + _logger.WriteLine($"errors: {string.Join(Environment.NewLine, result.Errors ?? Array.Empty())}"); + _logger.WriteLine($"warnings: {string.Join(Environment.NewLine, result.Warnings ?? Array.Empty())}"); + _logger.WriteLine($"path: {result.Path}"); + _logger.WriteLine($"version: {result.Version}"); + } + + private sealed class MockContext : SdkResolverContext + { + } + + private sealed class MockFactory : SdkResultFactory + { + public override SdkResult IndicateFailure(IEnumerable errors, IEnumerable warnings = null) + => new MockResult { Success = false, Errors = errors, Warnings = warnings }; + + public override SdkResult IndicateSuccess(string path, string version, IEnumerable warnings = null) + => new MockResult { Success = true, Path = path, Version = version, Warnings = warnings }; + } + + private sealed class MockResult : SdkResult + { + public new bool Success { get => base.Success; set => base.Success = value; } + public string Version { get; set; } + public string Path { get; set; } + public IEnumerable Errors { get; set; } + public IEnumerable Warnings { get; set; } + } + } +} diff --git a/test/Microsoft.DotNet.MSBuildSdkResolver.Tests/Microsoft.DotNet.MSBuildSdkResolver.Tests.csproj b/test/Microsoft.DotNet.MSBuildSdkResolver.Tests/Microsoft.DotNet.MSBuildSdkResolver.Tests.csproj new file mode 100644 index 000000000..e302505e4 --- /dev/null +++ b/test/Microsoft.DotNet.MSBuildSdkResolver.Tests/Microsoft.DotNet.MSBuildSdkResolver.Tests.csproj @@ -0,0 +1,29 @@ + + + + + net46;$(CliTargetFramework) + $(CliTargetFramework) + $(CLI_SharedFrameworkVersion) + Exe + ../../tools/Key.snk + true + true + + + + + + + + + + + + + + + + + + diff --git a/test/Microsoft.DotNet.MSBuildSdkResolver.Tests/xunit.runner.json b/test/Microsoft.DotNet.MSBuildSdkResolver.Tests/xunit.runner.json new file mode 100644 index 000000000..34b2fe2cd --- /dev/null +++ b/test/Microsoft.DotNet.MSBuildSdkResolver.Tests/xunit.runner.json @@ -0,0 +1,3 @@ +{ + "shadowCopy": false +} \ No newline at end of file From afd1d8ed66a15a43ec9ef36fadba172df9ab646e Mon Sep 17 00:00:00 2001 From: Nick Guerrera Date: Wed, 26 Apr 2017 23:28:57 -0700 Subject: [PATCH 2/5] Reduce build console output spew --- run-build.ps1 | 2 +- run-build.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/run-build.ps1 b/run-build.ps1 index 0c3ed5443..e97c49c1e 100644 --- a/run-build.ps1 +++ b/run-build.ps1 @@ -124,6 +124,6 @@ if ($NoBuild) else { dotnet msbuild build.proj /p:Architecture=$Architecture /p:GeneratePropsFile=true /t:WriteDynamicPropsToStaticPropsFiles - dotnet msbuild build.proj /m /v:diag /fl /flp:v=diag /p:Architecture=$Architecture $ExtraParameters + dotnet msbuild build.proj /m /v:normal /fl /flp:v=diag /p:Architecture=$Architecture $ExtraParameters if($LASTEXITCODE -ne 0) { throw "Failed to build" } } diff --git a/run-build.sh b/run-build.sh index 16cece2d1..77034b6a6 100755 --- a/run-build.sh +++ b/run-build.sh @@ -203,7 +203,7 @@ echo "${args[@]}" if [ $BUILD -eq 1 ]; then dotnet msbuild build.proj /p:Architecture=$ARCHITECTURE $CUSTOM_BUILD_ARGS /p:GeneratePropsFile=true /t:WriteDynamicPropsToStaticPropsFiles - dotnet msbuild build.proj /m /v:diag /fl /flp:v=diag /p:Architecture=$ARCHITECTURE $CUSTOM_BUILD_ARGS "${args[@]}" + dotnet msbuild build.proj /m /v:normal /fl /flp:v=diag /p:Architecture=$ARCHITECTURE $CUSTOM_BUILD_ARGS "${args[@]}" else echo "Not building due to --nobuild" echo "Command that would be run is: 'dotnet msbuild build.proj /m /p:Architecture=$ARCHITECTURE $CUSTOM_BUILD_ARGS ${args[@]}'" From 77ab1c58574bae03a68347d73e33fe799091afaa Mon Sep 17 00:00:00 2001 From: Nick Guerrera Date: Thu, 27 Apr 2017 22:28:32 -0700 Subject: [PATCH 3/5] Add option to skip LZMA build --- src/redist/redist.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/redist/redist.csproj b/src/redist/redist.csproj index b8bd12306..bc00e7e3f 100644 --- a/src/redist/redist.csproj +++ b/src/redist/redist.csproj @@ -148,6 +148,7 @@ From fa4fe3b2c6e006d570551741df2a2b01da73e87d Mon Sep 17 00:00:00 2001 From: Nick Guerrera Date: Thu, 27 Apr 2017 23:05:48 -0700 Subject: [PATCH 4/5] Package MSBuild SDK resolver in VS insertion nupkg --- build/Compile.targets | 7 +++++++ build/MSBuildExtensions.targets | 8 ++------ build/OutputDirectories.props | 1 + build/package/Installer.MSI.targets | 4 ++-- .../Microsoft.DotNet.MSBuildSdkResolver.csproj | 7 ++++--- .../Microsoft.DotNet.MSBuildSdkResolver.Tests.csproj | 1 + 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/build/Compile.targets b/build/Compile.targets index b44ed8380..d2a3747aa 100644 --- a/build/Compile.targets +++ b/build/Compile.targets @@ -13,6 +13,13 @@ + + + + diff --git a/build/MSBuildExtensions.targets b/build/MSBuildExtensions.targets index 9b99b96b9..b8d2d97e9 100644 --- a/build/MSBuildExtensions.targets +++ b/build/MSBuildExtensions.targets @@ -9,7 +9,6 @@ - 15.0/Imports/Microsoft.Common.props/ImportBefore Microsoft.NETCoreSdk.BundledVersions.props @@ -59,12 +58,9 @@ Copyright (c) .NET Foundation. All rights reserved. - - + - diff --git a/build/OutputDirectories.props b/build/OutputDirectories.props index 99a5666cf..8d3df1307 100644 --- a/build/OutputDirectories.props +++ b/build/OutputDirectories.props @@ -14,5 +14,6 @@ $(RepoRoot)/artifacts/testpackages/ $(OutputDirectory)/dotnet$(ExeExtension) $(IntermediateDirectory)/GeneratedMSBuildExtensions + $(IntermediateDirectory)/MSBuildSdkResolver diff --git a/build/package/Installer.MSI.targets b/build/package/Installer.MSI.targets index 86e81c22b..c132efdb0 100644 --- a/build/package/Installer.MSI.targets +++ b/build/package/Installer.MSI.targets @@ -146,13 +146,13 @@ diff --git a/src/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj b/src/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj index b6399659d..335eed3ce 100644 --- a/src/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj +++ b/src/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj @@ -12,11 +12,12 @@ true true false + $(SdkResolverOutputDirectory) - - + + @@ -35,4 +36,4 @@ - \ No newline at end of file + diff --git a/test/Microsoft.DotNet.MSBuildSdkResolver.Tests/Microsoft.DotNet.MSBuildSdkResolver.Tests.csproj b/test/Microsoft.DotNet.MSBuildSdkResolver.Tests/Microsoft.DotNet.MSBuildSdkResolver.Tests.csproj index e302505e4..bfc8f571a 100644 --- a/test/Microsoft.DotNet.MSBuildSdkResolver.Tests/Microsoft.DotNet.MSBuildSdkResolver.Tests.csproj +++ b/test/Microsoft.DotNet.MSBuildSdkResolver.Tests/Microsoft.DotNet.MSBuildSdkResolver.Tests.csproj @@ -16,6 +16,7 @@ + From 09dd14bfe467e1cd264740af6ed4a8a243ccb53a Mon Sep 17 00:00:00 2001 From: Nick Guerrera Date: Fri, 28 Apr 2017 10:37:20 -0700 Subject: [PATCH 5/5] Fix some comments and add missing space to error message --- .../MSBuildSdkResolver.cs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs b/src/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs index fefbd46d0..6048d3d59 100644 --- a/src/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs +++ b/src/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs @@ -32,7 +32,7 @@ namespace Microsoft.DotNet.MSBuildSdkResolver new[] { "Unable to locate the .NET Core SDK. Check that it is installed and that the version" - + "specified in global.json (if any) matches the installed version." + + " specified in global.json (if any) matches the installed version." }); } @@ -70,19 +70,22 @@ namespace Microsoft.DotNet.MSBuildSdkResolver return null; } - // Search for [ProgramFiles]\dotnet in this order. Only ProgramFiles is defined on + // Search for [ProgramFiles]\dotnet in this order. private static readonly string[] s_programFiles = new[] { - // "c:\Program Files" on x64 machine regardless process bitness, undefined on x86 machines. + // "c:\Program Files" on x64 machine regardless process architecture. + // Undefined on x86 machines. "ProgramW6432", - // "c:\Program Files (x86)" on x64 machine regardless of process bitness, undefined on x64 machines. + // "c:\Program Files (x86)" on x64 machine regardless of process architecture + // Undefined on x86 machines. "ProgramFiles(x86)", - // "c:\Program Files" in x64 process, "c:\Program Files (x86)" in x86 process. - // hostfxr will search this on its own if multilevel lookup is not disable, but - // we do it explicitly to prevent an environment with disabled multilevel lookup - // from crippling desktop msbuild and VS. + // "c:\Program Files" or "C:\Program Files (x86)" on x64 machine depending on process architecture. + // "c:\Program Files" on x86 machines (therefore not redundant with the two locations above in that case). + // + // NOTE: hostfxr will search this on its own if multilevel lookup is not disable, but we do it explicitly + // to prevent an environment with disabled multilevel lookup from crippling desktop msbuild and VS. "ProgramFiles", }; @@ -91,12 +94,12 @@ namespace Microsoft.DotNet.MSBuildSdkResolver string environmentOverride = Environment.GetEnvironmentVariable("DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR"); if (environmentOverride != null) { - return new List(1) { environmentOverride }; + return new List(capacity: 1) { environmentOverride }; } // Initial capacity is 2 because while there are 3 candidates, we expect at most 2 unique ones (x64 + x86) // Also, N=3 here means that we needn't be concerned with the O(N^2) complexity of the foreach + contains. - var candidates = new List(2); + var candidates = new List(capacity: 2); foreach (string variable in s_programFiles) { string directory = Environment.GetEnvironmentVariable(variable);