diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/InternalsVisibleToAndStrongNameTests.cs b/src/Compilers/CSharp/Test/Emit/Attributes/InternalsVisibleToAndStrongNameTests.cs index 7134320d58e584b2a6016d4f353444121d11e877..ce6583c34651f905cc20d6a9e9b0ea8ffaef2e55 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/InternalsVisibleToAndStrongNameTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/InternalsVisibleToAndStrongNameTests.cs @@ -1373,7 +1373,7 @@ private void ConfirmModuleAttributePresentAndAddingToAssemblyResultsInSignedOutp //confirm file does not claim to be signed Assert.Equal(0, (int)(flags & CorFlags.StrongNameSigned)); - var corlibName = CoreClrShim.IsRunningOnCoreClr ? "netstandard" : "mscorlib"; + var corlibName = RuntimeUtilities.IsCoreClrRuntime ? "netstandard" : "mscorlib"; EntityHandle token = metadata.Module.GetTypeRef(metadata.Module.GetAssemblyRef(corlibName), "System.Runtime.CompilerServices", "AssemblyAttributesGoHere"); Assert.False(token.IsNil); //could the type ref be located? If not then the attribute's not there. var attrInfos = metadata.Module.FindTargetAttributes(token, expectedModuleAttr); diff --git a/src/Compilers/CSharp/csc/csc.csproj b/src/Compilers/CSharp/csc/csc.csproj index aae3403699da11554a3fb8c734ec6762b0538222..b48d639273a12d3aca928848417775766e51692d 100644 --- a/src/Compilers/CSharp/csc/csc.csproj +++ b/src/Compilers/CSharp/csc/csc.csproj @@ -27,6 +27,7 @@ + BuildClient.cs diff --git a/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs b/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs index 24cecfc222abd8cff26b1bd9e96911d8ef96a4a6..9ef17a319d5d62973278c96da2c8d8efbcc20cc7 100644 --- a/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs +++ b/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.IO; using Microsoft.Build.Utilities; namespace Microsoft.CodeAnalysis.BuildTasks @@ -17,6 +18,8 @@ public abstract class ManagedToolTask : ToolTask protected abstract string PathToManagedTool { get; } + protected string PathToManagedToolWithoutExtension => Path.ChangeExtension(PathToManagedTool, string.Empty); + /// /// Note: "Native" here does not necessarily mean "native binary". /// "Native" in this context means "native invocation", and running the executable directly. @@ -31,14 +34,9 @@ public abstract class ManagedToolTask : ToolTask protected sealed override string GenerateCommandLineCommands() { var commandLineArguments = ToolArguments; - if (IsManagedTool && IsCliHost(out string pathToDotnet)) + if (IsManagedTool) { - var pathToTool = PathToManagedTool; - if (pathToTool is null) - { - Log.LogErrorWithCodeFromResources("General_ToolFileNotFound", ToolName); - } - commandLineArguments = PrependFileToArgs(pathToTool, commandLineArguments); + (_, commandLineArguments, _) = RuntimeHostInfo.GetProcessInfo(PathToManagedToolWithoutExtension, commandLineArguments); } return commandLineArguments; @@ -51,21 +49,9 @@ protected sealed override string GenerateCommandLineCommands() /// protected sealed override string GenerateFullPathToTool() { - if (IsManagedTool) - { - if (IsCliHost(out string pathToDotnet)) - { - return pathToDotnet; - } - else - { - return PathToManagedTool; - } - } - else - { - return PathToNativeTool; - } + return IsManagedTool + ? RuntimeHostInfo.GetProcessInfo(PathToManagedToolWithoutExtension, string.Empty).ProcessFilePath + : PathToNativeTool; } protected abstract string ToolNameWithoutExtension { get; } @@ -80,37 +66,6 @@ protected sealed override string GenerateFullPathToTool() /// as the implementation of IsManagedTool calls this property. See the comment in /// . /// - protected sealed override string ToolName => $"{ToolNameWithoutExtension}.{ToolExtension}"; - - private static string ToolExtension => -#if NETCOREAPP2_1 - "dll"; -#elif NET472 - "exe"; -#else -#error Unrecognized framework -#endif - - private static bool IsCliHost(out string pathToDotnet) - { -#if NETCOREAPP2_1 - pathToDotnet = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH"); - return !string.IsNullOrEmpty(pathToDotnet); -#elif NET472 - pathToDotnet = null; - return false; -#else -#error Unrecognized framework -#endif - } - - private static string PrependFileToArgs(string pathToTool, string commandLineArgs) - { - var builder = new CommandLineBuilderExtension(); - builder.AppendFileNameIfNotNull(pathToTool); - builder.AppendTextUnquoted(" "); - builder.AppendTextUnquoted(commandLineArgs); - return builder.ToString(); - } + protected sealed override string ToolName => $"{ToolNameWithoutExtension}.{RuntimeHostInfo.ToolExtension}"; } } diff --git a/src/Compilers/Core/MSBuildTask/Microsoft.Build.Tasks.CodeAnalysis.csproj b/src/Compilers/Core/MSBuildTask/Microsoft.Build.Tasks.CodeAnalysis.csproj index 75946d58471c841d5e2c87df089eb8d8cf77d9f0..1343d67962b68d8fc5d61d83990c3f3fd51ebfcd 100644 --- a/src/Compilers/Core/MSBuildTask/Microsoft.Build.Tasks.CodeAnalysis.csproj +++ b/src/Compilers/Core/MSBuildTask/Microsoft.Build.Tasks.CodeAnalysis.csproj @@ -35,19 +35,11 @@ - - BuildServerConnection.cs - - - CoreClrShim.cs - + + - - CommandLineUtilities.cs - - - CompilerOptionParseUtilities.cs - + + IReadOnlySet.cs diff --git a/src/Compilers/Core/MSBuildTask/Utilities.cs b/src/Compilers/Core/MSBuildTask/Utilities.cs index f361a1ede68631509595981d63c38f8d06511f55..52121df8d2d45cdd6d2014a6b64c0ec9c9ed8078 100644 --- a/src/Compilers/Core/MSBuildTask/Utilities.cs +++ b/src/Compilers/Core/MSBuildTask/Utilities.cs @@ -191,49 +191,17 @@ internal static string TryGetAssemblyPath(Assembly assembly) } /// - /// Try to get the directory this assembly is in. Returns null if assembly - /// was in the GAC or DLL location can not be retrieved. + /// Generate the full path to the tool that is deployed with our build tasks. /// - public static string GenerateFullPathToTool(string toolName) + internal static string GenerateFullPathToTool(string toolName) { - string toolLocation = null; - var buildTask = typeof(Utilities).GetTypeInfo().Assembly; - var assemblyPath = TryGetAssemblyPath(buildTask); - - if (assemblyPath != null) - { - var assemblyDirectory = Path.GetDirectoryName(assemblyPath); - var desktopToolLocalLocation = Path.Combine(assemblyDirectory, toolName); - var cliToolLocalLocation = Path.Combine(assemblyDirectory, "bincore", toolName); - - if (File.Exists(desktopToolLocalLocation)) - { - toolLocation = desktopToolLocalLocation; - } - else if (File.Exists(cliToolLocalLocation)) - { - toolLocation = cliToolLocalLocation; - } - } - - if (toolLocation == null) - { - // Roslyn only deploys to the 32Bit folder of MSBuild, so request this path on all architectures. - var pathToBuildTools = ToolLocationHelper.GetPathToBuildTools(ToolLocationHelper.CurrentToolsVersion, DotNetFrameworkArchitecture.Bitness32); - - if (pathToBuildTools != null) - { - var toolMSBuildLocation = Path.Combine(pathToBuildTools, MSBuildRoslynFolderName, toolName); - - if (File.Exists(toolMSBuildLocation)) - { - toolLocation = toolMSBuildLocation; - } - } - } + var assemblyPath = buildTask.Location; + var assemblyDirectory = Path.GetDirectoryName(assemblyPath); - return toolLocation; + return RuntimeHostInfo.IsDesktopRuntime + ? Path.Combine(assemblyDirectory, toolName) + : Path.Combine(assemblyDirectory, "bincore", toolName); } } } diff --git a/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj b/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj index 4438a5ea232bbdc1134dc9242e474c951fb5ac46..535e940b0442acf5484ab57820527f19d16bbe52 100644 --- a/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj +++ b/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj @@ -36,7 +36,6 @@ - DesktopShim.cs diff --git a/src/Compilers/Server/VBCSCompiler/VBCSCompiler.csproj b/src/Compilers/Server/VBCSCompiler/VBCSCompiler.csproj index c1dc3a824d91283de72fa1584d150f3181c5afab..51d2cbb235c7ec3224bf59345ef8ff198eddf3b5 100644 --- a/src/Compilers/Server/VBCSCompiler/VBCSCompiler.csproj +++ b/src/Compilers/Server/VBCSCompiler/VBCSCompiler.csproj @@ -28,6 +28,7 @@ + BuildClient.cs diff --git a/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs b/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs index 6a2493d856bc29b22ad76d145635c74ff4564e37..32d17693c5bebc0e8ae348b50cd856c57fc15416 100644 --- a/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs +++ b/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs @@ -223,7 +223,7 @@ private static DisposableFile GetResultFile(TempDirectory directory, string resu private static void RunCompilerOutput(TempFile file, string expectedOutput) { - if (!CoreClrShim.IsRunningOnCoreClr) + if (RuntimeHostInfo.IsDesktopRuntime) { var result = ProcessUtilities.Run(file.Path, "", Path.GetDirectoryName(file.Path)); Assert.Equal(expectedOutput.Trim(), result.Output.Trim()); diff --git a/src/Compilers/Shared/BuildClient.cs b/src/Compilers/Shared/BuildClient.cs index 7280f557f13efdf3798e6ed4ae16ccb76674f055..a278febb5c6d2135466a9dd4f8fc3de7d07ff692 100644 --- a/src/Compilers/Shared/BuildClient.cs +++ b/src/Compilers/Shared/BuildClient.cs @@ -14,6 +14,7 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CommandLine { @@ -48,14 +49,9 @@ internal abstract class BuildClient /// public static string GetSystemSdkDirectory() { - if (CoreClrShim.IsRunningOnCoreClr) - { - return null; - } - else - { - return RuntimeEnvironment.GetRuntimeDirectory(); - } + return RuntimeHostInfo.IsCoreClrRuntime + ? null + : RuntimeEnvironment.GetRuntimeDirectory(); } /// @@ -236,12 +232,12 @@ private static bool UseNativeArguments() return false; } - if (Type.GetType("Mono.Runtime") != null) + if (PlatformInformation.IsRunningOnMono) { return false; } - if (CoreClrShim.IsRunningOnCoreClr) + if (RuntimeHostInfo.IsCoreClrRuntime) { // The native invoke ends up giving us both CoreRun and the exe file. // We've decided to ignore backcompat for CoreCLR, diff --git a/src/Compilers/Shared/BuildServerConnection.cs b/src/Compilers/Shared/BuildServerConnection.cs index 097c7b3f196ea33cf72a828ff47de972323aa627..40e5f5e6317b932dfec95c514251dac4a41011ef 100644 --- a/src/Compilers/Shared/BuildServerConnection.cs +++ b/src/Compilers/Shared/BuildServerConnection.cs @@ -56,9 +56,6 @@ internal BuildPathsAlt(string clientDir, string workingDir, string sdkDir, strin internal sealed class BuildServerConnection { - internal const string ServerNameDesktop = "VBCSCompiler.exe"; - internal const string ServerNameCoreClr = "VBCSCompiler.dll"; - // Spend up to 1s connecting to existing process (existing processes should be always responsive). internal const int TimeOutMsExistingProcess = 1000; @@ -374,30 +371,13 @@ internal static bool IsCompilerServerSupported(string tempPath) internal static bool TryCreateServerCore(string clientDir, string pipeName) { - string expectedPath; - string processArguments; -#if NETCOREAPP2_1 - // The server should be in the same directory as the client - var expectedCompilerPath = Path.Combine(clientDir, ServerNameCoreClr); - expectedPath = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? "dotnet"; - processArguments = $@"""{expectedCompilerPath}"" ""-pipename:{pipeName}"""; - - if (!File.Exists(expectedCompilerPath)) - { - return false; - } -#elif NET472 - // The server should be in the same directory as the client - expectedPath = Path.Combine(clientDir, ServerNameDesktop); - processArguments = $@"""-pipename:{pipeName}"""; + var serverPathWithoutExetnsion = Path.Combine(clientDir, "VBCSCompiler"); + var serverInfo = RuntimeHostInfo.GetProcessInfo(serverPathWithoutExetnsion, $"-pipename:{pipeName}"); - if (!File.Exists(expectedPath)) + if (!File.Exists(serverInfo.ToolFilePath)) { return false; } -#else -#error Unrecognized configuration -#endif if (PlatformInformation.IsWindows) { @@ -415,9 +395,9 @@ internal static bool TryCreateServerCore(string clientDir, string pipeName) PROCESS_INFORMATION processInfo; - Log("Attempting to create process '{0}'", expectedPath); + Log("Attempting to create process '{0}'", serverInfo.ProcessFilePath); - var builder = new StringBuilder($@"""{expectedPath}"" {processArguments}"); + var builder = new StringBuilder($@"""{serverInfo.ProcessFilePath}"" {serverInfo.CommandLineArguments}"); bool success = CreateProcess( lpApplicationName: null, @@ -449,8 +429,8 @@ internal static bool TryCreateServerCore(string clientDir, string pipeName) { var startInfo = new ProcessStartInfo() { - FileName = expectedPath, - Arguments = processArguments, + FileName = serverInfo.ProcessFilePath, + Arguments = serverInfo.CommandLineArguments, UseShellExecute = false, WorkingDirectory = clientDir, RedirectStandardInput = true, diff --git a/src/Compilers/Shared/CoreClrAnalyzerAssemblyLoader.cs b/src/Compilers/Shared/CoreClrAnalyzerAssemblyLoader.cs index 2ecb0a6922862af2408a75b3d9604649fc158305..ccf3220ee4cbc1f7c0f6841edcacf7b90c3c0205 100644 --- a/src/Compilers/Shared/CoreClrAnalyzerAssemblyLoader.cs +++ b/src/Compilers/Shared/CoreClrAnalyzerAssemblyLoader.cs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -#if NETCOREAPP1_1 || NETCOREAPP2_1 +#if NETCOREAPP2_1 using System.Diagnostics; using System.Reflection; diff --git a/src/Compilers/Shared/DesktopBuildClient.cs b/src/Compilers/Shared/DesktopBuildClient.cs index abb8217643fb820405c76a2692433418925fc397..e2587836d2ec898d2c8a201dd296ef1a370c5260 100644 --- a/src/Compilers/Shared/DesktopBuildClient.cs +++ b/src/Compilers/Shared/DesktopBuildClient.cs @@ -29,7 +29,7 @@ internal DesktopBuildClient(RequestLanguage language, CompileFunc compileFunc, I internal static int Run(IEnumerable arguments, RequestLanguage language, CompileFunc compileFunc, IAnalyzerAssemblyLoader analyzerAssemblyLoader) { var sdkDir = GetSystemSdkDirectory(); - if (CoreClrShim.IsRunningOnCoreClr) + if (RuntimeHostInfo.IsCoreClrRuntime) { // Register encodings for console // https://github.com/dotnet/roslyn/issues/10785 diff --git a/src/Compilers/Shared/RuntimeHostInfo.cs b/src/Compilers/Shared/RuntimeHostInfo.cs new file mode 100644 index 0000000000000000000000000000000000000000..0be41945b41bd18d67aef24a9de7a2677cfb880c --- /dev/null +++ b/src/Compilers/Shared/RuntimeHostInfo.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.IO.Pipes; + +namespace Microsoft.CodeAnalysis +{ + /// + /// This type provides information about the runtime which is hosting application. It must be included in a concrete + /// target framework to be used. + /// + internal static class RuntimeHostInfo + { + internal static bool IsCoreClrRuntime => !IsDesktopRuntime; + + internal static string ToolExtension => IsCoreClrRuntime ? "dll" : "exe"; + + /// + /// This gets information about invoking a tool on the current runtime. This will attempt to + /// execute a tool as an EXE when on desktop and using dotnet when on CoreClr. + /// + internal static (string ProcessFilePath, string CommandLineArguments, string ToolFilePath) GetProcessInfo(string toolFilePathWithoutExtension, string commandLineArguments) + { + Debug.Assert(!toolFilePathWithoutExtension.EndsWith(".dll") && !toolFilePathWithoutExtension.EndsWith(".exe")); + + var toolFilePath = $"{toolFilePathWithoutExtension}.{ToolExtension}"; + if (IsDotnetHost(out string pathToDotNet)) + { + commandLineArguments = $@"exec ""{toolFilePath}"" {commandLineArguments}"; + return (pathToDotNet, commandLineArguments, toolFilePath); + } + else + { + return (toolFilePath, commandLineArguments, toolFilePath); + } + } + +#if NET472 + internal static bool IsDesktopRuntime => true; + + internal static bool IsDotnetHost(out string pathToDotnet) + { + pathToDotnet = null; + return false; + } + + internal static NamedPipeClientStream CreateNamedPipeClient(string serverName, string pipeName, PipeDirection direction, PipeOptions options) => + new NamedPipeClientStream(serverName, pipeName, direction, options); + +#elif NETCOREAPP2_1 + internal static bool IsDesktopRuntime => false; + + internal static string DotnetHostPathEnvironmentName = "DOTNET_HOST_PATH"; + + internal static bool IsDotnetHost(out string pathToDotnet) + { + pathToDotnet = GetDotnetPathOrDefault(); + return true; + } + + /// + /// Get the path to the dotnet executable. This will throw in the case it is not properly setup + /// by the environment. + /// + internal static string GetDotnetPath() + { + var pathToDotnet = Environment.GetEnvironmentVariable(DotnetHostPathEnvironmentName); + if (string.IsNullOrEmpty(pathToDotnet)) + { + throw new InvalidOperationException($"{DotnetHostPathEnvironmentName} is not set"); + } + return pathToDotnet; + } + + /// + /// Get the path to the dotnet executable. In the case the host did not provide this information + /// in the environment this will return simply "dotnet". + /// + internal static string GetDotnetPathOrDefault() + { + var pathToDotnet = Environment.GetEnvironmentVariable(DotnetHostPathEnvironmentName); + return pathToDotnet ?? "dotnet"; + } +#else +#error Unsupported configuration +#endif + } +} diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index 2dc674cb156392482e16a927efaae4a26580203b..ae2f31e1c55516d08685a25fe17274378431a444 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -891,7 +891,7 @@ public static SyntaxTree ParseWithRoundTripCheck(string text, CSharpParseOptions string assemblyName = "", string sourceFileName = "") { - IEnumerable allReferences = CoreClrShim.IsRunningOnCoreClr + IEnumerable allReferences = RuntimeUtilities.IsCoreClrRuntime ? TargetFrameworkUtil.NetStandard20References : TargetFrameworkUtil.Mscorlib461ExtendedReferences.Add(TestReferences.Net461.netstandardRef); diff --git a/src/Compilers/VisualBasic/vbc/vbc.csproj b/src/Compilers/VisualBasic/vbc/vbc.csproj index 4811cc462a5ff264f6871c7e4e7b209e17d8d90c..dbfeccd6356a1d178c9f52606aef13ba7c2b22c7 100644 --- a/src/Compilers/VisualBasic/vbc/vbc.csproj +++ b/src/Compilers/VisualBasic/vbc/vbc.csproj @@ -26,6 +26,7 @@ + BuildClient.cs diff --git a/src/Scripting/Core/Microsoft.CodeAnalysis.Scripting.csproj b/src/Scripting/Core/Microsoft.CodeAnalysis.Scripting.csproj index 6e95d46a7234eeaadeecb86f4890715af94f64fc..415542e7415ef7e0d5de6870f96cbaa5fa296797 100644 --- a/src/Scripting/Core/Microsoft.CodeAnalysis.Scripting.csproj +++ b/src/Scripting/Core/Microsoft.CodeAnalysis.Scripting.csproj @@ -34,6 +34,7 @@ + Hosting\Resolvers\RelativePathResolver.cs diff --git a/src/Test/Utilities/Portable/Assert/ConditionalFactAttribute.cs b/src/Test/Utilities/Portable/Assert/ConditionalFactAttribute.cs index 0ef2f28409fd4fdbf057624472415a9e381d7626..f4bb7d54e8c0213de1747b70140a6870e270ef4f 100644 --- a/src/Test/Utilities/Portable/Assert/ConditionalFactAttribute.cs +++ b/src/Test/Utilities/Portable/Assert/ConditionalFactAttribute.cs @@ -137,7 +137,7 @@ public static class ExecutionConditionUtil { public static bool IsWindows => Path.DirectorySeparatorChar == '\\'; public static bool IsUnix => !IsWindows; - public static bool IsDesktop => CoreClrShim.AssemblyLoadContext.Type == null; + public static bool IsDesktop => RuntimeUtilities.IsDesktopRuntime; public static bool IsWindowsDesktop => IsWindows && IsDesktop; public static bool IsMonoDesktop => Type.GetType("Mono.Runtime") != null; public static bool IsCoreClr => !IsDesktop;