// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//-----------------------------------------------------------------------
//
// Class holding the parameters and settings which are global to the build.
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading;
using System.Globalization;
using Microsoft.Build.BackEnd;
using Microsoft.Build.Collections;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Framework;
using Microsoft.Build.Internal;
using Microsoft.Build.Shared;
using ForwardingLoggerRecord = Microsoft.Build.Logging.ForwardingLoggerRecord;
namespace Microsoft.Build.Execution
{
using Utilities = Microsoft.Build.Internal.Utilities;
///
/// This class represents all of the settings which must be specified to start a build.
///
public class BuildParameters : INodePacketTranslatable
{
///
/// The default thread stack size for threads owned by MSBuild.
///
private const int DefaultThreadStackSize = 262144; // 256k
///
/// The timeout for endpoints to shut down.
///
private const int DefaultEndpointShutdownTimeout = 30 * 1000; // 30 seconds
///
/// The timeout for the engine to shutdown.
///
private const int DefaultEngineShutdownTimeout = Timeout.Infinite;
///
/// The shutdown timeout for the logging thread.
///
private const int DefaultLoggingThreadShutdownTimeout = 30 * 1000; // 30 seconds
///
/// The shutdown timeout for the request builder.
///
private const int DefaultRequestBuilderShutdownTimeout = Timeout.Infinite;
///
/// The maximum number of idle request builders to retain before we start discarding them.
///
private const int DefaultIdleRequestBuilderLimit = 2;
///
/// The startup directory.
///
private static string s_startupDirectory = NativeMethodsShared.GetCurrentDirectory();
///
/// Indicates whether we should warn when a property is uninitialized when it is used.
///
private static bool? s_warnOnUninitializedProperty;
///
/// Indicates if we should dump string interning stats.
///
private static bool? s_dumpOpportunisticInternStats;
///
/// Indicates if we should debug the expander.
///
private static bool? s_debugExpansion;
///
/// Indicates if we should keep duplicate target outputs.
///
private static bool? s_keepDuplicateOutputs;
///
/// Indicates if we should enable the build plan
///
private static bool? s_enableBuildPlan;
///
/// The maximum number of idle request builders we will retain.
///
private static int? s_idleRequestBuilderLimit;
///
/// Location that msbuild.exe was last successfully found at.
///
private static string s_msbuildExeKnownToExistAt;
///
/// The build id
///
private int _buildId = 0;
///
/// The culture
///
private CultureInfo _culture = CultureInfo.CurrentCulture;
///
/// The default tools version.
///
private string _defaultToolsVersion = "2.0";
///
/// When true, causes the build to emit a summary of project build information
///
private bool _detailedSummary;
///
/// Flag indicating whether node reuse should be enabled.
/// By default, it is enabled.
///
#if FEATURE_NODE_REUSE
private bool _enableNodeReuse = true;
#else
private bool _enableNodeReuse = false;
#endif
///
/// The original process environment.
///
private Dictionary _buildProcessEnvironment;
///
/// The environment properties for the build.
///
private PropertyDictionary _environmentProperties = new PropertyDictionary();
///
/// The forwarding logger records.
///
private IEnumerable _forwardingLoggers;
///
/// The build-global properties.
///
private PropertyDictionary _globalProperties = new PropertyDictionary();
///
/// The host services object.
///
private HostServices _hostServices;
///
/// The loggers.
///
private IEnumerable _loggers;
///
/// The maximum number of nodes to use.
///
private int _maxNodeCount = 1;
///
/// The maximum amount of memory to use.
///
private int _memoryUseLimit = 0; // Unlimited
///
/// The location of the node exe. This is the full path including the exe file itself.
///
private string _nodeExeLocation;
///
/// Flag indicating if we should only log critical events.
///
private bool _onlyLogCriticalEvents = false;
///
/// A list of warnings to treat as errors.
///
private ISet _warningsAsErrors;
///
/// A list of warnings to treat as low importance messages.
///
private ISet _warningsAsMessages;
///
/// The location of the toolset definitions.
///
private ToolsetDefinitionLocations _toolsetDefinitionLocations = ToolsetDefinitionLocations.Default;
///
/// The UI culture.
///
private CultureInfo _uiCulture = CultureInfo.CurrentUICulture;
///
/// The toolset provider
///
private ToolsetProvider _toolsetProvider;
///
/// Should the operating environment such as the current directory and environment be saved and restored between project builds and task invocations
/// This should be defaulted to true as we should normally do this. This should be set to false for GlobalDTAR which could run at the same time in a different build manager.
///
private bool _saveOperatingEnvironment = true;
///
/// Should the logging service be done Synchronously when the number of cps's is 1
///
private bool _useSynchronousLogging = false;
///
/// Should the inprocess node be shutdown when the build finishes. By default this is false
/// since visual studio needs to keep the inprocess node around after the build has finished.
///
private bool _shutdownInProcNodeOnBuildFinish = false;
///
/// When true, the in-proc node will not be available.
///
private bool _disableInProcNode = false;
///
/// When true, the build should log task inputs to the loggers.
///
private bool _logTaskInputs = false;
///
/// When true, the build should log the input parameters. Note - logging these is very expensive!
///
private bool _logInitialPropertiesAndItems = false;
///
/// The settings used to load the project under build
///
private ProjectLoadSettings _projectLoadSettings = ProjectLoadSettings.Default;
///
/// Constructor for those who intend to set all properties themselves.
///
public BuildParameters()
{
Initialize(Utilities.GetEnvironmentProperties(), new ProjectRootElementCache(false), null);
}
///
/// Creates BuildParameters from a ProjectCollection.
///
/// The ProjectCollection from which the BuildParameters should populate itself.
public BuildParameters(ProjectCollection projectCollection)
{
ErrorUtilities.VerifyThrowArgumentNull(projectCollection, "projectCollection");
Initialize(new PropertyDictionary(projectCollection.EnvironmentProperties), projectCollection.ProjectRootElementCache, new ToolsetProvider(projectCollection.Toolsets));
_maxNodeCount = projectCollection.MaxNodeCount;
_onlyLogCriticalEvents = projectCollection.OnlyLogCriticalEvents;
_toolsetDefinitionLocations = projectCollection.ToolsetLocations;
_defaultToolsVersion = projectCollection.DefaultToolsVersion;
_globalProperties = new PropertyDictionary(projectCollection.GlobalPropertiesCollection);
}
///
/// Private constructor for translation
///
private BuildParameters(INodePacketTranslator translator)
{
((INodePacketTranslatable)this).Translate(translator);
}
///
/// Copy constructor
///
private BuildParameters(BuildParameters other)
{
ErrorUtilities.VerifyThrowInternalNull(other, "other");
_buildId = other._buildId;
_culture = other._culture;
_defaultToolsVersion = other._defaultToolsVersion;
_enableNodeReuse = other._enableNodeReuse;
_buildProcessEnvironment = other._buildProcessEnvironment != null ? new Dictionary(other._buildProcessEnvironment) : null;
_environmentProperties = other._environmentProperties != null ? new PropertyDictionary(other._environmentProperties) : null;
_forwardingLoggers = other._forwardingLoggers != null ? new List(other._forwardingLoggers) : null;
_globalProperties = other._globalProperties != null ? new PropertyDictionary(other._globalProperties) : null;
_hostServices = other._hostServices;
_loggers = other._loggers != null ? new List(other._loggers) : null;
_maxNodeCount = other._maxNodeCount;
_memoryUseLimit = other._memoryUseLimit;
_nodeExeLocation = other._nodeExeLocation;
NodeId = other.NodeId;
_onlyLogCriticalEvents = other._onlyLogCriticalEvents;
#if FEATURE_THREAD_PRIORITY
BuildThreadPriority = other.BuildThreadPriority;
#endif
_toolsetProvider = other._toolsetProvider;
_toolsetDefinitionLocations = other._toolsetDefinitionLocations;
_toolsetProvider = other._toolsetProvider;
_uiCulture = other._uiCulture;
_detailedSummary = other._detailedSummary;
_shutdownInProcNodeOnBuildFinish = other._shutdownInProcNodeOnBuildFinish;
ProjectRootElementCache = other.ProjectRootElementCache;
ResetCaches = other.ResetCaches;
LegacyThreadingSemantics = other.LegacyThreadingSemantics;
_saveOperatingEnvironment = other._saveOperatingEnvironment;
_useSynchronousLogging = other._useSynchronousLogging;
_disableInProcNode = other._disableInProcNode;
_logTaskInputs = other._logTaskInputs;
_logInitialPropertiesAndItems = other._logInitialPropertiesAndItems;
_warningsAsErrors = other._warningsAsErrors == null ? null : new HashSet(other._warningsAsErrors, StringComparer.OrdinalIgnoreCase);
_warningsAsMessages = other._warningsAsMessages == null ? null : new HashSet(other._warningsAsMessages, StringComparer.OrdinalIgnoreCase);
_projectLoadSettings = other._projectLoadSettings;
}
#if FEATURE_THREAD_PRIORITY
///
/// Gets or sets the desired thread priority for building.
///
public ThreadPriority BuildThreadPriority { get; set; } = ThreadPriority.Normal;
#endif
///
/// By default if the number of processes is set to 1 we will use Asynchronous logging. However if we want to use synchronous logging when the number of cpu's is set to 1
/// this property needs to be set to true.
///
public bool UseSynchronousLogging
{
get => _useSynchronousLogging;
set => _useSynchronousLogging = value;
}
///
/// Gets the environment variables which were set when this build was created.
///
public IDictionary BuildProcessEnvironment => new ReadOnlyDictionary(
_buildProcessEnvironment ?? new Dictionary(0));
///
/// The name of the culture to use during the build.
///
public CultureInfo Culture
{
get => _culture;
set => _culture = value;
}
///
/// The default tools version for the build.
///
public string DefaultToolsVersion
{
get => _defaultToolsVersion;
set => _defaultToolsVersion = value;
}
///
/// When true, indicates that the build should emit a detailed summary at the end of the log.
///
public bool DetailedSummary
{
get => _detailedSummary;
set => _detailedSummary = value;
}
///
/// When true, indicates the in-proc node should not be used.
///
public bool DisableInProcNode
{
get => _disableInProcNode;
set => _disableInProcNode = value;
}
///
/// When true, indicates that the task parameters should be logged.
///
public bool LogTaskInputs
{
get => _logTaskInputs;
set => _logTaskInputs = value;
}
///
/// When true, indicates that the initial properties and items should be logged.
///
public bool LogInitialPropertiesAndItems
{
get => _logInitialPropertiesAndItems;
set => _logInitialPropertiesAndItems = value;
}
///
/// Indicates that the build should reset the configuration and results caches.
///
public bool ResetCaches
{
get;
set;
}
///
/// Flag indicating whether out-of-proc nodes should remain after the build and wait for further builds.
///
public bool EnableNodeReuse
{
get => _enableNodeReuse;
set => _enableNodeReuse = value;
}
///
/// Gets an immutable collection of environment properties.
///
///
/// This differs from the BuildProcessEnvironment in that there are certain MSBuild-specific properties which are added, and those environment variables which
/// would not be valid as MSBuild properties are removed.
///
public IDictionary EnvironmentProperties
{
get
{
return new ReadOnlyConvertingDictionary(_environmentProperties,
instance => ((IProperty) instance).EvaluatedValueEscaped);
}
}
///
/// The collection of forwarding logger descriptions.
///
public IEnumerable ForwardingLoggers
{
get => _forwardingLoggers;
set
{
if (value != null)
{
foreach (ForwardingLoggerRecord logger in value)
{
ErrorUtilities.VerifyThrowArgumentNull(logger, "ForwardingLoggers", "NullLoggerNotAllowed");
}
}
_forwardingLoggers = value;
}
}
///
/// Sets or retrieves an immutable collection of global properties.
///
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification =
"Accessor returns a readonly collection, and the BuildParameters class is immutable.")]
public IDictionary GlobalProperties
{
get
{
return new ReadOnlyConvertingDictionary(_globalProperties,
instance => ((IProperty) instance).EvaluatedValueEscaped);
}
set
{
_globalProperties = new PropertyDictionary(value.Count);
foreach (KeyValuePair property in value)
{
_globalProperties[property.Key] = ProjectPropertyInstance.Create(property.Key, property.Value);
}
}
}
///
/// Interface allowing the host to provide additional control over the build process.
///
public HostServices HostServices
{
get => _hostServices;
set => _hostServices = value;
}
///
/// Enables or disables legacy threading semantics
///
///
/// Legacy threading semantics indicate that if a submission is to be built
/// only on the in-proc node and the submission is executed synchronously, then all of its
/// requests will be built on the thread which invoked the build rather than a
/// thread owned by the BuildManager.
///
public bool LegacyThreadingSemantics
{
get;
set;
}
///
/// The collection of loggers to use during the build.
///
public IEnumerable Loggers
{
get => _loggers;
set
{
if (value != null)
{
foreach (ILogger logger in value)
{
ErrorUtilities.VerifyThrowArgumentNull(logger, "Loggers", "NullLoggerNotAllowed");
}
}
_loggers = value;
}
}
///
/// The maximum number of nodes this build may use.
///
public int MaxNodeCount
{
get => _maxNodeCount;
set
{
ErrorUtilities.VerifyThrowArgument(value > 0, "InvalidMaxNodeCount");
_maxNodeCount = value;
}
}
///
/// The amount of memory the build should limit itself to using, in megabytes.
///
public int MemoryUseLimit
{
get => _memoryUseLimit;
set => _memoryUseLimit = value;
}
///
/// The location of the build node executable.
///
public string NodeExeLocation
{
get => _nodeExeLocation;
set => _nodeExeLocation = value;
}
///
/// Flag indicating if non-critical logging events should be discarded.
///
public bool OnlyLogCriticalEvents
{
get => _onlyLogCriticalEvents;
set => _onlyLogCriticalEvents = value;
}
///
/// A list of warnings to treat as errors. To treat all warnings as errors, set this to an empty .
///
public ISet WarningsAsErrors
{
get => _warningsAsErrors;
set => _warningsAsErrors = value;
}
///
/// A list of warnings to treat as low importance messages.
///
public ISet WarningsAsMessages
{
get => _warningsAsMessages;
set => _warningsAsMessages = value;
}
///
/// Locations to search for toolsets.
///
public ToolsetDefinitionLocations ToolsetDefinitionLocations
{
get => _toolsetDefinitionLocations;
set => _toolsetDefinitionLocations = value;
}
///
/// Returns all of the toolsets.
///
///
/// toolsetProvider.Toolsets is already a readonly collection.
///
public ICollection Toolsets => _toolsetProvider.Toolsets;
///
/// The name of the UI culture to use during the build.
///
public CultureInfo UICulture
{
get => _uiCulture;
set => _uiCulture = value;
}
///
/// Flag indicating if the operating environment such as the current directory and environment be saved and restored between project builds and task invocations.
/// This should be set to false for any other build managers running in the system so that we do not have two build managers trampling on each others environment.
///
public bool SaveOperatingEnvironment
{
get => _saveOperatingEnvironment;
set => _saveOperatingEnvironment = value;
}
///
/// Shutdown the inprocess node when the build finishes. By default this is false
/// since visual studio needs to keep the inprocess node around after the build finishes.
///
public bool ShutdownInProcNodeOnBuildFinish
{
get => _shutdownInProcNodeOnBuildFinish;
set => _shutdownInProcNodeOnBuildFinish = value;
}
///
/// Gets the internal msbuild thread stack size.
///
internal static int ThreadStackSize => CommunicationsUtilities.GetIntegerVariableOrDefault(
"MSBUILDTHREADSTACKSIZE", DefaultThreadStackSize);
///
/// Gets the endpoint shutdown timeout.
///
internal static int EndpointShutdownTimeout => CommunicationsUtilities.GetIntegerVariableOrDefault(
"MSBUILDENDPOINTSHUTDOWNTIMEOUT", DefaultEndpointShutdownTimeout);
///
/// Gets or sets the engine shutdown timeout.
///
internal static int EngineShutdownTimeout => CommunicationsUtilities.GetIntegerVariableOrDefault(
"MSBUILDENGINESHUTDOWNTIMEOUT", DefaultEngineShutdownTimeout);
///
/// Gets the maximum number of idle request builders to retain.
///
internal static int IdleRequestBuilderLimit => GetStaticIntVariableOrDefault("MSBUILDIDLEREQUESTBUILDERLIMIT",
ref s_idleRequestBuilderLimit, DefaultIdleRequestBuilderLimit);
///
/// Gets the logging thread shutdown timeout.
///
internal static int LoggingThreadShutdownTimeout => CommunicationsUtilities.GetIntegerVariableOrDefault(
"MSBUILDLOGGINGTHREADSHUTDOWNTIMEOUT", DefaultLoggingThreadShutdownTimeout);
///
/// Gets the request builder shutdown timeout.
///
internal static int RequestBuilderShutdownTimeout => CommunicationsUtilities.GetIntegerVariableOrDefault(
"MSBUILDREQUESTBUILDERSHUTDOWNTIMEOUT", DefaultRequestBuilderShutdownTimeout);
///
/// Gets the startup directory.
///
internal static string StartupDirectory => s_startupDirectory;
///
/// Indicates whether the build plan is enabled or not.
///
internal static bool EnableBuildPlan => GetStaticBoolVariableOrDefault("MSBUILDENABLEBUILDPLAN",
ref s_enableBuildPlan, false);
///
/// Indicates whether we should warn when a property is uninitialized when it is used.
///
internal static bool WarnOnUninitializedProperty
{
get => GetStaticBoolVariableOrDefault("MSBUILDWARNONUNINITIALIZEDPROPERTY",
ref s_warnOnUninitializedProperty, false);
set => s_warnOnUninitializedProperty = value;
}
///
/// Indicates whether we should dump string interning stats
///
internal static bool DumpOpportunisticInternStats => GetStaticBoolVariableOrDefault(
"MSBUILDDUMPOPPORTUNISTICINTERNSTATS", ref s_dumpOpportunisticInternStats, false);
///
/// Indicates whether we should dump debugging information about the expander
///
internal static bool DebugExpansion => GetStaticBoolVariableOrDefault("MSBUILDDEBUGEXPANSION",
ref s_debugExpansion, false);
///
/// Indicates whether we should keep duplicate target outputs
///
internal static bool KeepDuplicateOutputs => GetStaticBoolVariableOrDefault("MSBUILDKEEPDUPLICATEOUTPUTS",
ref s_keepDuplicateOutputs, false);
///
/// Gets or sets the build id.
///
internal int BuildId
{
get => _buildId;
set => _buildId = value;
}
///
/// Gets or sets the environment properties.
///
///
/// This is not the same as BuildProcessEnvironment. See EnvironmentProperties. These properties are those which
/// are used during evaluation of a project, and exclude those properties which would not be valid MSBuild properties
/// because they contain invalid characters (such as 'Program Files (x86)').
///
internal PropertyDictionary EnvironmentPropertiesInternal
{
get => _environmentProperties;
set
{
ErrorUtilities.VerifyThrowInternalNull(value, "EnvironmentPropertiesInternal");
_environmentProperties = value;
}
}
///
/// Gets the global properties.
///
internal PropertyDictionary GlobalPropertiesInternal => _globalProperties;
///
/// Gets or sets the node id.
///
internal int NodeId { get; set; } = 0;
///
/// Gets the toolset provider.
///
internal IToolsetProvider ToolsetProvider
{
get
{
EnsureToolsets();
return _toolsetProvider;
}
}
///
/// The one and only project root element cache to be used for the build.
///
internal ProjectRootElementCache ProjectRootElementCache
{
get;
set;
}
#if FEATURE_APPDOMAIN
///
/// Information for configuring child AppDomains.
///
internal AppDomainSetup AppDomainSetup
{
get;
set;
}
#endif
///
/// (for diagnostic use) Whether or not this is out of proc
///
internal bool IsOutOfProc
{
get;
set;
}
///
public ProjectLoadSettings ProjectLoadSettings
{
get => _projectLoadSettings;
set => _projectLoadSettings = value;
}
///
/// Retrieves a toolset.
///
public Toolset GetToolset(string toolsVersion)
{
EnsureToolsets();
return _toolsetProvider.GetToolset(toolsVersion);
}
///
/// Creates a clone of this BuildParameters object. This creates a clone of the logger collections, but does not deep clone
/// the loggers within.
///
public BuildParameters Clone()
{
return new BuildParameters(this);
}
///
/// Implementation of the serialization mechanism.
///
void INodePacketTranslatable.Translate(INodePacketTranslator translator)
{
translator.Translate(ref _buildId);
/* No build thread priority during translation. We specifically use the default (which is ThreadPriority.Normal) */
translator.TranslateDictionary(ref _buildProcessEnvironment, StringComparer.OrdinalIgnoreCase);
translator.TranslateCulture(ref _culture);
translator.Translate(ref _defaultToolsVersion);
translator.Translate(ref _disableInProcNode);
translator.Translate(ref _enableNodeReuse);
translator.TranslateProjectPropertyInstanceDictionary(ref _environmentProperties);
/* No forwarding logger information sent here - that goes with the node configuration */
translator.TranslateProjectPropertyInstanceDictionary(ref _globalProperties);
/* No host services during translation */
/* No loggers during translation */
translator.Translate(ref _maxNodeCount);
translator.Translate(ref _memoryUseLimit);
translator.Translate(ref _nodeExeLocation);
/* No node id during translation */
translator.Translate(ref _onlyLogCriticalEvents);
translator.Translate(ref s_startupDirectory);
translator.TranslateCulture(ref _uiCulture);
translator.Translate(ref _toolsetProvider, Microsoft.Build.Evaluation.ToolsetProvider.FactoryForDeserialization);
translator.Translate(ref _useSynchronousLogging);
translator.Translate(ref _shutdownInProcNodeOnBuildFinish);
translator.Translate(ref _logTaskInputs);
translator.Translate(ref _logInitialPropertiesAndItems);
translator.TranslateEnum(ref _projectLoadSettings, (int) _projectLoadSettings);
// ProjectRootElementCache is not transmitted.
// ResetCaches is not transmitted.
// LegacyThreadingSemantics is not transmitted.
}
#region INodePacketTranslatable Members
///
/// The class factory for deserialization.
///
internal static BuildParameters FactoryForDeserialization(INodePacketTranslator translator)
{
return new BuildParameters(translator);
}
#endregion
///
/// Gets the value of a boolean environment setting which is not expected to change.
///
private static bool GetStaticBoolVariableOrDefault(string environmentVariable, ref bool? backing, bool @default)
{
if (!backing.HasValue)
{
backing = !String.IsNullOrEmpty(Environment.GetEnvironmentVariable(environmentVariable)) || @default;
}
return backing.Value;
}
///
/// Gets the value of an integer environment variable, or returns the default if none is set or it cannot be converted.
///
private static int GetStaticIntVariableOrDefault(string environmentVariable, ref int? backingValue, int defaultValue)
{
if (!backingValue.HasValue)
{
string environmentValue = Environment.GetEnvironmentVariable(environmentVariable);
if (String.IsNullOrEmpty(environmentValue))
{
backingValue = defaultValue;
}
else
{
backingValue = Int32.TryParse(environmentValue, out var parsedValue) ? parsedValue : defaultValue;
}
}
return backingValue.Value;
}
///
/// Centralization of the common parts of construction.
///
private void Initialize(PropertyDictionary environmentProperties, ProjectRootElementCache projectRootElementCache, ToolsetProvider toolsetProvider)
{
_buildProcessEnvironment = CommunicationsUtilities.GetEnvironmentVariables();
_environmentProperties = environmentProperties;
ProjectRootElementCache = projectRootElementCache;
ResetCaches = true;
_toolsetProvider = toolsetProvider;
if (Environment.GetEnvironmentVariable("MSBUILDDISABLENODEREUSE") == "1") // For example to disable node reuse within Visual Studio
{
_enableNodeReuse = false;
}
if (Environment.GetEnvironmentVariable("MSBUILDDETAILEDSUMMARY") == "1") // For example to get detailed summary within Visual Studio
{
_detailedSummary = true;
}
_nodeExeLocation = FindMSBuildExe();
}
///
/// Loads the toolsets if we don't have them already.
///
private void EnsureToolsets()
{
if (_toolsetProvider != null)
{
return;
}
_toolsetProvider = new ToolsetProvider(DefaultToolsVersion, _environmentProperties, _globalProperties, ToolsetDefinitionLocations);
}
///
/// This method determines where MSBuild.Exe is and sets the NodeExePath to that by default.
///
private string FindMSBuildExe()
{
string location = _nodeExeLocation;
// Use the location specified by the user in code.
if (!string.IsNullOrEmpty(location) && CheckMSBuildExeExistsAt(location))
{
return location;
}
// Try what we think is the current executable path.
return BuildEnvironmentHelper.Instance.CurrentMSBuildExePath;
}
///
/// Helper to avoid doing an expensive disk check for MSBuild.exe when
/// we already checked in a previous build.
/// This File.Exists otherwise can show up in profiles when there's a lot of
/// design time builds going on.
///
private bool CheckMSBuildExeExistsAt(string path)
{
if (s_msbuildExeKnownToExistAt != null && string.Equals(path, s_msbuildExeKnownToExistAt, StringComparison.OrdinalIgnoreCase))
{
// We found it there last time: it must exist there.
return true;
}
if (File.Exists(path))
{
s_msbuildExeKnownToExistAt = path;
return true;
}
return false;
}
}
}