public static Task <BuildResponse> RunServerCompilationAsync( Guid requestId, RequestLanguage language, string?sharedCompilationId, List <string> arguments, BuildPathsAlt buildPaths, string?keepAlive, string?libEnvVariable, ICompilerServerLogger logger, CancellationToken cancellationToken) { var pipeNameOpt = sharedCompilationId ?? GetPipeNameForPath(buildPaths.ClientDirectory); return(RunServerCompilationCoreAsync( requestId, language, arguments, buildPaths, pipeNameOpt, keepAlive, libEnvVariable, timeoutOverride: null, createServerFunc: TryCreateServerCore, logger: logger, cancellationToken: cancellationToken)); }
private static Task <BuildResponse> RunServerCompilationCore( RequestLanguage language, List <string> arguments, BuildPaths buildPaths, string pipeName, string keepAlive, string libEnvVariable, int?timeoutOverride, Func <string, string, bool> tryCreateServerFunc, CancellationToken cancellationToken) { var alt = new BuildPathsAlt( buildPaths.ClientDirectory, buildPaths.WorkingDirectory, buildPaths.SdkDirectory, buildPaths.TempDirectory); return(BuildServerConnection.RunServerCompilationCore( language, arguments, alt, pipeName, keepAlive, libEnvVariable, timeoutOverride, tryCreateServerFunc, cancellationToken)); }
internal static async Task <BuildResponse> RunServerCompilationCoreAsync( Guid requestId, RequestLanguage language, List <string> arguments, BuildPathsAlt buildPaths, string?pipeName, string?keepAlive, string?libDirectory, int?timeoutOverride, CreateServerFunc createServerFunc, ICompilerServerLogger logger, CancellationToken cancellationToken) { if (pipeName is null) { throw new ArgumentException(nameof(pipeName)); } if (buildPaths.TempDirectory == null) { throw new ArgumentException(nameof(buildPaths)); } // early check for the build hash. If we can't find it something is wrong; no point even trying to go to the server if (string.IsNullOrWhiteSpace(BuildProtocolConstants.GetCommitHash())) { return(new IncorrectHashBuildResponse()); } var pipeTask = tryConnectToServer(pipeName, buildPaths, timeoutOverride, createServerFunc, logger, cancellationToken); if (pipeTask is null) { return(new RejectedBuildResponse("Failed to connect to server")); } else { using var pipe = await pipeTask.ConfigureAwait(false); if (pipe is null) { return(new RejectedBuildResponse("Failed to connect to server")); } else { var request = BuildRequest.Create(language, arguments, workingDirectory: buildPaths.WorkingDirectory, tempDirectory: buildPaths.TempDirectory, compilerHash: BuildProtocolConstants.GetCommitHash() ?? "", requestId: requestId, keepAlive: keepAlive, libDirectory: libDirectory); return(await TryCompileAsync(pipe, request, logger, cancellationToken).ConfigureAwait(false)); } }
protected override int RunLocalCompilation(string[] arguments, BuildPathsAlt buildPaths, TextWriter textWriter) { return(_compileFunc(arguments, buildPaths, textWriter, #if DESKTOP new DesktopAnalyzerAssemblyLoader() #else ReflCoreClrAnalyzerAssemblyLoader.ctor() #endif )); }
internal static async Task <BuildResponse> RunServerCompilationCore( RequestLanguage language, List <string> arguments, BuildPathsAlt buildPaths, string pipeName, string keepAlive, string libEnvVariable, int?timeoutOverride, CreateServerFunc createServerFunc, CancellationToken cancellationToken) { if (pipeName == null) { return(new RejectedBuildResponse()); } if (buildPaths.TempDirectory == null) { return(new RejectedBuildResponse()); } // early check for the build hash. If we can't find it something is wrong; no point even trying to go to the server if (string.IsNullOrWhiteSpace(BuildProtocolConstants.GetCommitHash())) { return(new IncorrectHashBuildResponse()); } var pipeTask = tryConnectToServer(pipeName, buildPaths, timeoutOverride, createServerFunc, cancellationToken); if (pipeTask is null) { return(new RejectedBuildResponse()); } else { var pipe = await pipeTask.ConfigureAwait(false); if (pipe is null) { return(new RejectedBuildResponse()); } else { var request = BuildRequest.Create(language, buildPaths.WorkingDirectory, buildPaths.TempDirectory, BuildProtocolConstants.GetCommitHash(), arguments, keepAlive, libEnvVariable); return(await TryCompile(pipe, request, cancellationToken).ConfigureAwait(false)); } }
internal static int Run(IEnumerable <string> arguments, IEnumerable <string> extraArguments, RequestLanguage language, CompileFunc compileFunc, IAnalyzerAssemblyLoader analyzerAssemblyLoader) { var client = new DesktopBuildClient(language, compileFunc, analyzerAssemblyLoader); var clientDir = AppDomain.CurrentDomain.BaseDirectory; var sdkDir = RuntimeEnvironment.GetRuntimeDirectory(); var workingDir = Directory.GetCurrentDirectory(); string tempPath = Path.GetTempPath(); var buildPaths = new BuildPathsAlt(clientDir: clientDir, workingDir: workingDir, sdkDir: sdkDir, tempDir: tempPath); var originalArguments = BuildClient.GetCommandLineArgs(arguments).Concat(extraArguments).ToArray(); return(client.RunCompilation(originalArguments, buildPaths).ExitCode); }
internal static int Run(IEnumerable <string> arguments, RequestLanguage language, CompileFunc compileFunc) { // Should be using BuildClient.GetCommandLineArgs(arguments) here. But the native invoke // ends up giving us both CoreRun and the exe file. Need to find a good way to remove the host // as well as the EXE argument. // https://github.com/dotnet/roslyn/issues/6677 var client = new CoreClrBuildClient(language, compileFunc); var clientDir = AppContext.BaseDirectory; var workingDir = Directory.GetCurrentDirectory(); var buildPaths = new BuildPathsAlt(clientDir: clientDir, workingDir: workingDir, sdkDir: null, tempDir: null); return(client.RunCompilation(arguments, buildPaths).ExitCode); }
public static Task <BuildResponse> RunServerCompilation( RequestLanguage language, List <string> arguments, BuildPathsAlt buildPaths, string keepAlive, string libEnvVariable, CancellationToken cancellationToken) { var pipeNameOpt = GetPipeNameForPathOpt(buildPaths.ClientDirectory); return(RunServerCompilationCore( language, arguments, buildPaths, pipeNameOpt, keepAlive, libEnvVariable, timeoutOverride: null, tryCreateServerFunc: TryCreateServerCore, cancellationToken: cancellationToken)); }
public static Task<BuildResponse> RunServerCompilation( RequestLanguage language, List<string> arguments, BuildPathsAlt buildPaths, string keepAlive, string libEnvVariable, CancellationToken cancellationToken) { var pipeNameOpt = GetPipeNameForPathOpt(buildPaths.ClientDirectory); return RunServerCompilationCore( language, arguments, buildPaths, pipeNameOpt, keepAlive, libEnvVariable, timeoutOverride: null, tryCreateServerFunc: TryCreateServerCore, cancellationToken: cancellationToken); }
internal static Task<BuildResponse> RunServerCompilationCore( RequestLanguage language, List<string> arguments, BuildPathsAlt buildPaths, string pipeName, string keepAlive, string libEnvVariable, int? timeoutOverride, Func<string, string, bool> tryCreateServerFunc, CancellationToken cancellationToken) { if (pipeName == null) { return Task.FromResult<BuildResponse>(new RejectedBuildResponse()); } if (buildPaths.TempDirectory == null) { return Task.FromResult<BuildResponse>(new RejectedBuildResponse()); } var clientDir = buildPaths.ClientDirectory; var timeoutNewProcess = timeoutOverride ?? TimeOutMsNewProcess; var timeoutExistingProcess = timeoutOverride ?? TimeOutMsExistingProcess; var clientMutexName = GetClientMutexName(pipeName); bool holdsMutex; using (var clientMutex = new Mutex(initiallyOwned: true, name: clientMutexName, createdNew: out holdsMutex)) { try { if (!holdsMutex) { try { holdsMutex = clientMutex.WaitOne(timeoutNewProcess); if (!holdsMutex) { return Task.FromResult<BuildResponse>(new RejectedBuildResponse()); } } catch (AbandonedMutexException) { holdsMutex = true; } } // Check for an already running server var serverMutexName = GetServerMutexName(pipeName); bool wasServerRunning = WasServerMutexOpen(serverMutexName); var timeout = wasServerRunning ? timeoutExistingProcess : timeoutNewProcess; NamedPipeClientStream pipe = null; if (wasServerRunning || tryCreateServerFunc(clientDir, pipeName)) { pipe = TryConnectToServer(pipeName, timeout, cancellationToken); } if (pipe != null) { var request = BuildRequest.Create(language, buildPaths.WorkingDirectory, buildPaths.TempDirectory, arguments, keepAlive, libEnvVariable); return TryCompile(pipe, request, cancellationToken); } } finally { if (holdsMutex) { clientMutex.ReleaseMutex(); } } } return Task.FromResult<BuildResponse>(new RejectedBuildResponse()); }
/// <summary> /// Run a compilation through the compiler server and print the output /// to the console. If the compiler server fails, run the fallback /// compiler. /// </summary> internal RunCompilationResult RunCompilation(IEnumerable <string> originalArguments, BuildPathsAlt buildPaths, TextWriter textWriter = null) { textWriter = textWriter ?? Console.Out; var args = originalArguments.Select(arg => arg.Trim()).ToArray(); bool hasShared; string keepAlive; string errorMessage; string sessionKey; List <string> parsedArgs; if (!ReflCommandLineParser.TryParseClientArgs( args, out parsedArgs, out hasShared, out keepAlive, out sessionKey, out errorMessage)) { Console.Out.WriteLine(errorMessage); return(RunCompilationResult.Failed); } // It's okay, and expected, for the server compilation to fail. In that case just fall // back to normal compilation. var exitCode = RunLocalCompilation(parsedArgs.ToArray(), buildPaths, textWriter); return(new RunCompilationResult(exitCode)); }
protected abstract string GetSessionKey(BuildPathsAlt buildPaths);
protected override int RunLocalCompilation(string[] arguments, BuildPathsAlt buildPaths, TextWriter textWriter) { return(_compileFunc(arguments, buildPaths, textWriter, _analyzerAssemblyLoader)); }
internal static async Task <BuildResponse> RunServerCompilationCore( RequestLanguage language, List <string> arguments, BuildPathsAlt buildPaths, string pipeName, string keepAlive, string libEnvVariable, int?timeoutOverride, CreateServerFunc createServerFunc, CancellationToken cancellationToken) { if (pipeName == null) { return(new RejectedBuildResponse()); } if (buildPaths.TempDirectory == null) { return(new RejectedBuildResponse()); } // early check for the build hash. If we can't find it something is wrong; no point even trying to go to the server if (string.IsNullOrWhiteSpace(BuildProtocolConstants.GetCommitHash())) { return(new IncorrectHashBuildResponse()); } var clientDir = buildPaths.ClientDirectory; var timeoutNewProcess = timeoutOverride ?? TimeOutMsNewProcess; var timeoutExistingProcess = timeoutOverride ?? TimeOutMsExistingProcess; Task <NamedPipeClientStream> pipeTask = null; IServerMutex clientMutex = null; try { var holdsMutex = false; try { var clientMutexName = GetClientMutexName(pipeName); clientMutex = OpenOrCreateMutex(clientMutexName, out holdsMutex); } catch { // The Mutex constructor can throw in certain cases. One specific example is docker containers // where the /tmp directory is restricted. In those cases there is no reliable way to execute // the server and we need to fall back to the command line. // // Example: https://github.com/dotnet/roslyn/issues/24124 return(new RejectedBuildResponse()); } if (!holdsMutex) { try { holdsMutex = clientMutex.TryLock(timeoutNewProcess); if (!holdsMutex) { return(new RejectedBuildResponse()); } } catch (AbandonedMutexException) { holdsMutex = true; } } // Check for an already running server var serverMutexName = GetServerMutexName(pipeName); bool wasServerRunning = WasServerMutexOpen(serverMutexName); var timeout = wasServerRunning ? timeoutExistingProcess : timeoutNewProcess; if (wasServerRunning || createServerFunc(clientDir, pipeName)) { pipeTask = TryConnectToServerAsync(pipeName, timeout, cancellationToken); } } finally { clientMutex?.Dispose(); } if (pipeTask != null) { var pipe = await pipeTask.ConfigureAwait(false); if (pipe != null) { var request = BuildRequest.Create(language, buildPaths.WorkingDirectory, buildPaths.TempDirectory, BuildProtocolConstants.GetCommitHash(), arguments, keepAlive, libEnvVariable); return(await TryCompile(pipe, request, cancellationToken).ConfigureAwait(false)); } } return(new RejectedBuildResponse()); }
private static Task<BuildResponse> RunServerCompilationCore( RequestLanguage language, List<string> arguments, BuildPaths buildPaths, string pipeName, string keepAlive, string libEnvVariable, int? timeoutOverride, Func<string, string, bool> tryCreateServerFunc, CancellationToken cancellationToken) { var alt = new BuildPathsAlt( buildPaths.ClientDirectory, buildPaths.WorkingDirectory, buildPaths.SdkDirectory, buildPaths.TempDirectory); return BuildServerConnection.RunServerCompilationCore( language, arguments, alt, pipeName, keepAlive, libEnvVariable, timeoutOverride, tryCreateServerFunc, cancellationToken); }
protected override string GetSessionKey(BuildPathsAlt buildPaths) { return(string.Empty); }
internal static Task <BuildResponse> RunServerCompilationCore( RequestLanguage language, List <string> arguments, BuildPathsAlt buildPaths, string pipeName, string keepAlive, string libEnvVariable, int?timeoutOverride, Func <string, string, bool> tryCreateServerFunc, CancellationToken cancellationToken) { if (pipeName == null) { return(Task.FromResult <BuildResponse>(new RejectedBuildResponse())); } if (buildPaths.TempDirectory == null) { return(Task.FromResult <BuildResponse>(new RejectedBuildResponse())); } var clientDir = buildPaths.ClientDirectory; var timeoutNewProcess = timeoutOverride ?? TimeOutMsNewProcess; var timeoutExistingProcess = timeoutOverride ?? TimeOutMsExistingProcess; var clientMutexName = GetClientMutexName(pipeName); bool holdsMutex; using (var clientMutex = new Mutex(initiallyOwned: true, name: clientMutexName, createdNew: out holdsMutex)) { try { if (!holdsMutex) { try { holdsMutex = clientMutex.WaitOne(timeoutNewProcess); if (!holdsMutex) { return(Task.FromResult <BuildResponse>(new RejectedBuildResponse())); } } catch (AbandonedMutexException) { holdsMutex = true; } } // Check for an already running server var serverMutexName = GetServerMutexName(pipeName); bool wasServerRunning = WasServerMutexOpen(serverMutexName); var timeout = wasServerRunning ? timeoutExistingProcess : timeoutNewProcess; NamedPipeClientStream pipe = null; if (wasServerRunning || tryCreateServerFunc(clientDir, pipeName)) { pipe = TryConnectToServer(pipeName, timeout, cancellationToken); } if (pipe != null) { var request = BuildRequest.Create(language, buildPaths.WorkingDirectory, buildPaths.TempDirectory, arguments, keepAlive, libEnvVariable); return(TryCompile(pipe, request, cancellationToken)); } } finally { if (holdsMutex) { clientMutex.ReleaseMutex(); } } } return(Task.FromResult <BuildResponse>(new RejectedBuildResponse())); }
protected override int ExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands) { if (ProvideCommandLineArgs) { CommandLineArgs = GetArguments(commandLineCommands, responseFileCommands) .Select(arg => new TaskItem(arg)).ToArray(); } if (SkipCompilerExecution) { return 0; } if (!UseSharedCompilation || !string.IsNullOrEmpty(ToolPath) || !BuildServerConnection.IsCompilerServerSupported) { return base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands); } using (_sharedCompileCts = new CancellationTokenSource()) { try { CompilerServerLogger.Log($"CommandLine = '{commandLineCommands}'"); CompilerServerLogger.Log($"BuildResponseFile = '{responseFileCommands}'"); var clientDir = Path.GetDirectoryName(pathToTool); // Note: we can't change the "tool path" printed to the console when we run // the Csc/Vbc task since MSBuild logs it for us before we get here. Instead, // we'll just print our own message that contains the real client location Log.LogMessage(ErrorString.UsingSharedCompilation, clientDir); var workingDir = CurrentDirectoryToUse(); var buildPaths = new BuildPathsAlt( clientDir: clientDir, // MSBuild doesn't need the .NET SDK directory sdkDir: null, workingDir: workingDir, tempDir: BuildServerConnection.GetTempPath(workingDir)); var responseTask = BuildServerConnection.RunServerCompilation( Language, GetArguments(commandLineCommands, responseFileCommands).ToList(), buildPaths, keepAlive: null, libEnvVariable: LibDirectoryToUse(), cancellationToken: _sharedCompileCts.Token); responseTask.Wait(_sharedCompileCts.Token); var response = responseTask.Result; if (response != null) { ExitCode = HandleResponse(response, pathToTool, responseFileCommands, commandLineCommands); } else { Log.LogMessage(ErrorString.SharedCompilationFallback, pathToTool); ExitCode = base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands); } } catch (OperationCanceledException) { ExitCode = 0; } catch (Exception e) { Log.LogErrorWithCodeFromResources("Compiler_UnexpectedException"); LogErrorOutput(e.ToString()); ExitCode = -1; } } return ExitCode; }
internal static async Task <BuildResponse> RunServerCompilationCore( RequestLanguage language, List <string> arguments, BuildPathsAlt buildPaths, string pipeName, string keepAlive, string libEnvVariable, int?timeoutOverride, Func <string, string, bool> tryCreateServerFunc, CancellationToken cancellationToken) { if (pipeName == null) { return(new RejectedBuildResponse()); } if (buildPaths.TempDirectory == null) { return(new RejectedBuildResponse()); } var clientDir = buildPaths.ClientDirectory; var timeoutNewProcess = timeoutOverride ?? TimeOutMsNewProcess; var timeoutExistingProcess = timeoutOverride ?? TimeOutMsExistingProcess; Task <NamedPipeClientStream> pipeTask = null; Mutex clientMutex = null; var holdsMutex = false; try { try { var clientMutexName = GetClientMutexName(pipeName); clientMutex = new Mutex(initiallyOwned: true, name: clientMutexName, out holdsMutex); } catch { // The Mutex constructor can throw in certain cases. One specific example is docker containers // where the /tmp directory is restricted. In those cases there is no reliable way to execute // the server and we need to fall back to the command line. // // Example: https://github.com/dotnet/roslyn/issues/24124 return(new RejectedBuildResponse()); } if (!holdsMutex) { try { holdsMutex = clientMutex.WaitOne(timeoutNewProcess); if (!holdsMutex) { return(new RejectedBuildResponse()); } } catch (AbandonedMutexException) { holdsMutex = true; } } // Check for an already running server var serverMutexName = GetServerMutexName(pipeName); bool wasServerRunning = WasServerMutexOpen(serverMutexName); var timeout = wasServerRunning ? timeoutExistingProcess : timeoutNewProcess; if (wasServerRunning || tryCreateServerFunc(clientDir, pipeName)) { pipeTask = TryConnectToServerAsync(pipeName, timeout, cancellationToken); } } finally { if (clientMutex != null) { if (holdsMutex) { clientMutex.ReleaseMutex(); } clientMutex.Dispose(); } } if (pipeTask != null) { var pipe = await pipeTask.ConfigureAwait(false); if (pipe != null) { var request = BuildRequest.Create(language, buildPaths.WorkingDirectory, buildPaths.TempDirectory, arguments, keepAlive, libEnvVariable); return(await TryCompile(pipe, request, cancellationToken).ConfigureAwait(false)); } } return(new RejectedBuildResponse()); }
public Task <RunCompilationResult> RunCompilationAsync(IEnumerable <string> originalArguments, BuildPathsAlt buildPaths, TextWriter textWriter = null) { var tcs = new TaskCompletionSource <RunCompilationResult>(); ThreadStart action = () => { try { var result = RunCompilation(originalArguments, buildPaths, textWriter); tcs.SetResult(result); } catch (Exception ex) { tcs.SetException(ex); } }; var thread = new Thread(action); thread.Start(); return(tcs.Task); }
// This code uses a Mutex.WaitOne / ReleaseMutex pairing. Both of these calls must occur on the same thread // or an exception will be thrown. This code lives in a separate non-async function to help ensure this // invariant doesn't get invalidated in the future by an `await` being inserted. static Task <NamedPipeClientStream> tryConnectToServer( string pipeName, BuildPathsAlt buildPaths, int?timeoutOverride, CreateServerFunc createServerFunc, CancellationToken cancellationToken) { var originalThreadId = Thread.CurrentThread.ManagedThreadId; var clientDir = buildPaths.ClientDirectory; var timeoutNewProcess = timeoutOverride ?? TimeOutMsNewProcess; var timeoutExistingProcess = timeoutOverride ?? TimeOutMsExistingProcess; Task <NamedPipeClientStream> pipeTask = null; IServerMutex clientMutex = null; try { var holdsMutex = false; try { var clientMutexName = GetClientMutexName(pipeName); clientMutex = OpenOrCreateMutex(clientMutexName, out holdsMutex); } catch { // The Mutex constructor can throw in certain cases. One specific example is docker containers // where the /tmp directory is restricted. In those cases there is no reliable way to execute // the server and we need to fall back to the command line. // // Example: https://github.com/dotnet/roslyn/issues/24124 return(null); } if (!holdsMutex) { try { holdsMutex = clientMutex.TryLock(timeoutNewProcess); if (!holdsMutex) { return(null); } } catch (AbandonedMutexException) { holdsMutex = true; } } // Check for an already running server var serverMutexName = GetServerMutexName(pipeName); bool wasServerRunning = WasServerMutexOpen(serverMutexName); var timeout = wasServerRunning ? timeoutExistingProcess : timeoutNewProcess; if (wasServerRunning || createServerFunc(clientDir, pipeName)) { pipeTask = TryConnectToServerAsync(pipeName, timeout, cancellationToken); } return(pipeTask); } finally { try { clientMutex?.Dispose(); } catch (ApplicationException e) { var releaseThreadId = Thread.CurrentThread.ManagedThreadId; var message = $"ReleaseMutex failed. WaitOne Id: {originalThreadId} Release Id: {releaseThreadId}"; throw new Exception(message, e); } } }
protected abstract int RunLocalCompilation(string[] arguments, BuildPathsAlt buildPaths, TextWriter textWriter);