| File: MultiThreadedTaskEnvironmentDriver.cs | Web Access |
| Project: src\msbuild\src\Framework\Microsoft.Build.Framework.csproj (Microsoft.Build.Framework) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using Microsoft.Build.Internal; namespace Microsoft.Build.Framework { /// <summary> /// Implementation of <see cref="ITaskEnvironmentDriver"/> that virtualizes environment variables and current directory /// for use in multithreaded mode where tasks may be executed in parallel. This allows each project to maintain its own /// isolated environment state without affecting other concurrently building projects. /// </summary> /// <remarks> /// This class is not accessed from multiple threads. Each msbuild thread node has its own instance to work with. /// </remarks> internal sealed class MultiThreadedTaskEnvironmentDriver : ITaskEnvironmentDriver { private readonly Dictionary<string, string> _environmentVariables; private AbsolutePath _currentDirectory; /// <summary> /// Initializes a new instance of the <see cref="MultiThreadedTaskEnvironmentDriver"/> class /// with the specified working directory and optional environment variables. /// </summary> /// <param name="currentDirectoryFullPath">The initial working directory.</param> /// <param name="environmentVariables">Dictionary of environment variables to use.</param> public MultiThreadedTaskEnvironmentDriver( string currentDirectoryFullPath, IDictionary<string, string> environmentVariables) { _environmentVariables = new Dictionary<string, string>(environmentVariables, CommunicationsUtilities.EnvironmentVariableComparer); ProjectDirectory = new AbsolutePath(currentDirectoryFullPath, ignoreRootedCheck: true); } /// <summary> /// Initializes a new instance of the <see cref="MultiThreadedTaskEnvironmentDriver"/> class /// with the specified working directory and environment variables from the current process. /// </summary> /// <param name="currentDirectoryFullPath">The initial working directory.</param> public MultiThreadedTaskEnvironmentDriver(string currentDirectoryFullPath) { IDictionary variables = Environment.GetEnvironmentVariables(); _environmentVariables = new Dictionary<string, string>(variables.Count, CommunicationsUtilities.EnvironmentVariableComparer); foreach (DictionaryEntry entry in variables) { _environmentVariables[(string)entry.Key] = (string)entry.Value!; } ProjectDirectory = new AbsolutePath(currentDirectoryFullPath, ignoreRootedCheck: true); } /// <inheritdoc/> public AbsolutePath ProjectDirectory { get => _currentDirectory; set { _currentDirectory = value.GetCanonicalForm(); // Keep the thread-static in sync for use by Expander and Modifiers during property/item expansion. // This allows Path.GetFullPath and %(FullPath) functions used in project files to resolve relative paths correctly in multithreaded mode. FileUtilities.CurrentThreadWorkingDirectory = _currentDirectory.Value; } } /// <inheritdoc/> public AbsolutePath GetAbsolutePath(string path) { return new AbsolutePath(path, ProjectDirectory); } /// <inheritdoc/> public string? GetEnvironmentVariable(string name) { return _environmentVariables.TryGetValue(name, out string? value) ? value : null; } /// <inheritdoc/> public IReadOnlyDictionary<string, string> GetEnvironmentVariables() { return _environmentVariables; } /// <inheritdoc/> public void SetEnvironmentVariable(string name, string? value) { if (value == null) { _environmentVariables.Remove(name); } else { _environmentVariables[name] = value; } } /// <inheritdoc/> public void SetEnvironment(IDictionary<string, string> newEnvironment) { if (ReferenceEquals(newEnvironment, _environmentVariables)) { return; } // Simply replace the entire environment dictionary _environmentVariables.Clear(); foreach (KeyValuePair<string, string> entry in newEnvironment) { _environmentVariables[entry.Key] = entry.Value; } } /// <inheritdoc/> public ProcessStartInfo GetProcessStartInfo() { var startInfo = new ProcessStartInfo { WorkingDirectory = ProjectDirectory.Value }; // Set environment variables foreach (var kvp in _environmentVariables) { startInfo.EnvironmentVariables[kvp.Key] = kvp.Value; } return startInfo; } /// <inheritdoc/> public void Dispose() { // Clear the thread-static to prevent pollution between builds on the same thread. FileUtilities.CurrentThreadWorkingDirectory = null; } } }