1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 //-----------------------------------------------------------------------
4 // </copyright>
5 // <summary>Class holding the parameters and settings which are global to the build.</summary>
6 //-----------------------------------------------------------------------
7 
8 using System;
9 using System.Collections.Generic;
10 using System.Collections.ObjectModel;
11 using System.Diagnostics.CodeAnalysis;
12 using System.IO;
13 using System.Threading;
14 using System.Globalization;
15 using Microsoft.Build.BackEnd;
16 using Microsoft.Build.Collections;
17 using Microsoft.Build.Evaluation;
18 using Microsoft.Build.Framework;
19 using Microsoft.Build.Internal;
20 using Microsoft.Build.Shared;
21 using ForwardingLoggerRecord = Microsoft.Build.Logging.ForwardingLoggerRecord;
22 
23 namespace Microsoft.Build.Execution
24 {
25     using Utilities = Microsoft.Build.Internal.Utilities;
26 
27     /// <summary>
28     /// This class represents all of the settings which must be specified to start a build.
29     /// </summary>
30     public class BuildParameters : INodePacketTranslatable
31     {
32         /// <summary>
33         /// The default thread stack size for threads owned by MSBuild.
34         /// </summary>
35         private const int DefaultThreadStackSize = 262144; // 256k
36 
37         /// <summary>
38         /// The timeout for endpoints to shut down.
39         /// </summary>
40         private const int DefaultEndpointShutdownTimeout = 30 * 1000; // 30 seconds
41 
42         /// <summary>
43         /// The timeout for the engine to shutdown.
44         /// </summary>
45         private const int DefaultEngineShutdownTimeout = Timeout.Infinite;
46 
47         /// <summary>
48         /// The shutdown timeout for the logging thread.
49         /// </summary>
50         private const int DefaultLoggingThreadShutdownTimeout = 30 * 1000; // 30 seconds
51 
52         /// <summary>
53         /// The shutdown timeout for the request builder.
54         /// </summary>
55         private const int DefaultRequestBuilderShutdownTimeout = Timeout.Infinite;
56 
57         /// <summary>
58         /// The maximum number of idle request builders to retain before we start discarding them.
59         /// </summary>
60         private const int DefaultIdleRequestBuilderLimit = 2;
61 
62         /// <summary>
63         /// The startup directory.
64         /// </summary>
65         private static string s_startupDirectory = NativeMethodsShared.GetCurrentDirectory();
66 
67         /// <summary>
68         /// Indicates whether we should warn when a property is uninitialized when it is used.
69         /// </summary>
70         private static bool? s_warnOnUninitializedProperty;
71 
72         /// <summary>
73         /// Indicates if we should dump string interning stats.
74         /// </summary>
75         private static bool? s_dumpOpportunisticInternStats;
76 
77         /// <summary>
78         /// Indicates if we should debug the expander.
79         /// </summary>
80         private static bool? s_debugExpansion;
81 
82         /// <summary>
83         /// Indicates if we should keep duplicate target outputs.
84         /// </summary>
85         private static bool? s_keepDuplicateOutputs;
86 
87         /// <summary>
88         /// Indicates if we should enable the build plan
89         /// </summary>
90         private static bool? s_enableBuildPlan;
91 
92         /// <summary>
93         /// The maximum number of idle request builders we will retain.
94         /// </summary>
95         private static int? s_idleRequestBuilderLimit;
96 
97         /// <summary>
98         /// Location that msbuild.exe was last successfully found at.
99         /// </summary>
100         private static string s_msbuildExeKnownToExistAt;
101 
102         /// <summary>
103         /// The build id
104         /// </summary>
105         private int _buildId = 0;
106 
107         /// <summary>
108         /// The culture
109         /// </summary>
110         private CultureInfo _culture = CultureInfo.CurrentCulture;
111 
112         /// <summary>
113         /// The default tools version.
114         /// </summary>
115         private string _defaultToolsVersion = "2.0";
116 
117         /// <summary>
118         /// When true, causes the build to emit a summary of project build information
119         /// </summary>
120         private bool _detailedSummary;
121 
122         /// <summary>
123         /// Flag indicating whether node reuse should be enabled.
124         /// By default, it is enabled.
125         /// </summary>
126 #if FEATURE_NODE_REUSE
127         private bool _enableNodeReuse = true;
128 #else
129         private bool _enableNodeReuse = false;
130 #endif
131 
132         /// <summary>
133         /// The original process environment.
134         /// </summary>
135         private Dictionary<string, string> _buildProcessEnvironment;
136 
137         /// <summary>
138         /// The environment properties for the build.
139         /// </summary>
140         private PropertyDictionary<ProjectPropertyInstance> _environmentProperties = new PropertyDictionary<ProjectPropertyInstance>();
141 
142         /// <summary>
143         /// The forwarding logger records.
144         /// </summary>
145         private IEnumerable<ForwardingLoggerRecord> _forwardingLoggers;
146 
147         /// <summary>
148         /// The build-global properties.
149         /// </summary>
150         private PropertyDictionary<ProjectPropertyInstance> _globalProperties = new PropertyDictionary<ProjectPropertyInstance>();
151 
152         /// <summary>
153         /// The host services object.
154         /// </summary>
155         private HostServices _hostServices;
156 
157         /// <summary>
158         /// The loggers.
159         /// </summary>
160         private IEnumerable<ILogger> _loggers;
161 
162         /// <summary>
163         /// The maximum number of nodes to use.
164         /// </summary>
165         private int _maxNodeCount = 1;
166 
167         /// <summary>
168         /// The maximum amount of memory to use.
169         /// </summary>
170         private int _memoryUseLimit = 0; // Unlimited
171 
172         /// <summary>
173         /// The location of the node exe.  This is the full path including the exe file itself.
174         /// </summary>
175         private string _nodeExeLocation;
176 
177         /// <summary>
178         /// Flag indicating if we should only log critical events.
179         /// </summary>
180         private bool _onlyLogCriticalEvents = false;
181 
182         /// <summary>
183         /// A list of warnings to treat as errors.
184         /// </summary>
185         private ISet<string> _warningsAsErrors;
186 
187         /// <summary>
188         /// A list of warnings to treat as low importance messages.
189         /// </summary>
190         private ISet<string> _warningsAsMessages;
191 
192         /// <summary>
193         /// The location of the toolset definitions.
194         /// </summary>
195         private ToolsetDefinitionLocations _toolsetDefinitionLocations = ToolsetDefinitionLocations.Default;
196 
197         /// <summary>
198         /// The UI culture.
199         /// </summary>
200         private CultureInfo _uiCulture = CultureInfo.CurrentUICulture;
201 
202         /// <summary>
203         /// The toolset provider
204         /// </summary>
205         private ToolsetProvider _toolsetProvider;
206 
207         /// <summary>
208         /// Should the operating environment such as the current directory and environment be saved and restored between project builds and task invocations
209         /// 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.
210         /// </summary>
211         private bool _saveOperatingEnvironment = true;
212 
213         /// <summary>
214         /// Should the logging service be done Synchronously when the number of cps's is 1
215         /// </summary>
216         private bool _useSynchronousLogging = false;
217 
218         /// <summary>
219         /// Should the inprocess node be shutdown when the build finishes. By default this is false
220         /// since visual studio needs to keep the inprocess node around after the build has finished.
221         /// </summary>
222         private bool _shutdownInProcNodeOnBuildFinish = false;
223 
224         /// <summary>
225         /// When true, the in-proc node will not be available.
226         /// </summary>
227         private bool _disableInProcNode = false;
228 
229         /// <summary>
230         /// When true, the build should log task inputs to the loggers.
231         /// </summary>
232         private bool _logTaskInputs = false;
233 
234         /// <summary>
235         /// When true, the build should log the input parameters.  Note - logging these is very expensive!
236         /// </summary>
237         private bool _logInitialPropertiesAndItems = false;
238 
239         /// <summary>
240         /// The settings used to load the project under build
241         /// </summary>
242         private ProjectLoadSettings _projectLoadSettings = ProjectLoadSettings.Default;
243 
244         /// <summary>
245         /// Constructor for those who intend to set all properties themselves.
246         /// </summary>
BuildParameters()247         public BuildParameters()
248         {
249             Initialize(Utilities.GetEnvironmentProperties(), new ProjectRootElementCache(false), null);
250         }
251 
252         /// <summary>
253         /// Creates BuildParameters from a ProjectCollection.
254         /// </summary>
255         /// <param name="projectCollection">The ProjectCollection from which the BuildParameters should populate itself.</param>
BuildParameters(ProjectCollection projectCollection)256         public BuildParameters(ProjectCollection projectCollection)
257         {
258             ErrorUtilities.VerifyThrowArgumentNull(projectCollection, "projectCollection");
259 
260             Initialize(new PropertyDictionary<ProjectPropertyInstance>(projectCollection.EnvironmentProperties), projectCollection.ProjectRootElementCache, new ToolsetProvider(projectCollection.Toolsets));
261 
262             _maxNodeCount = projectCollection.MaxNodeCount;
263             _onlyLogCriticalEvents = projectCollection.OnlyLogCriticalEvents;
264             _toolsetDefinitionLocations = projectCollection.ToolsetLocations;
265             _defaultToolsVersion = projectCollection.DefaultToolsVersion;
266 
267             _globalProperties = new PropertyDictionary<ProjectPropertyInstance>(projectCollection.GlobalPropertiesCollection);
268         }
269 
270         /// <summary>
271         /// Private constructor for translation
272         /// </summary>
BuildParameters(INodePacketTranslator translator)273         private BuildParameters(INodePacketTranslator translator)
274         {
275             ((INodePacketTranslatable)this).Translate(translator);
276         }
277 
278         /// <summary>
279         /// Copy constructor
280         /// </summary>
BuildParameters(BuildParameters other)281         private BuildParameters(BuildParameters other)
282         {
283             ErrorUtilities.VerifyThrowInternalNull(other, "other");
284 
285             _buildId = other._buildId;
286             _culture = other._culture;
287             _defaultToolsVersion = other._defaultToolsVersion;
288             _enableNodeReuse = other._enableNodeReuse;
289             _buildProcessEnvironment = other._buildProcessEnvironment != null ? new Dictionary<string, string>(other._buildProcessEnvironment) : null;
290             _environmentProperties = other._environmentProperties != null ? new PropertyDictionary<ProjectPropertyInstance>(other._environmentProperties) : null;
291             _forwardingLoggers = other._forwardingLoggers != null ? new List<ForwardingLoggerRecord>(other._forwardingLoggers) : null;
292             _globalProperties = other._globalProperties != null ? new PropertyDictionary<ProjectPropertyInstance>(other._globalProperties) : null;
293             _hostServices = other._hostServices;
294             _loggers = other._loggers != null ? new List<ILogger>(other._loggers) : null;
295             _maxNodeCount = other._maxNodeCount;
296             _memoryUseLimit = other._memoryUseLimit;
297             _nodeExeLocation = other._nodeExeLocation;
298             NodeId = other.NodeId;
299             _onlyLogCriticalEvents = other._onlyLogCriticalEvents;
300 #if FEATURE_THREAD_PRIORITY
301             BuildThreadPriority = other.BuildThreadPriority;
302 #endif
303             _toolsetProvider = other._toolsetProvider;
304             _toolsetDefinitionLocations = other._toolsetDefinitionLocations;
305             _toolsetProvider = other._toolsetProvider;
306             _uiCulture = other._uiCulture;
307             _detailedSummary = other._detailedSummary;
308             _shutdownInProcNodeOnBuildFinish = other._shutdownInProcNodeOnBuildFinish;
309             ProjectRootElementCache = other.ProjectRootElementCache;
310             ResetCaches = other.ResetCaches;
311             LegacyThreadingSemantics = other.LegacyThreadingSemantics;
312             _saveOperatingEnvironment = other._saveOperatingEnvironment;
313             _useSynchronousLogging = other._useSynchronousLogging;
314             _disableInProcNode = other._disableInProcNode;
315             _logTaskInputs = other._logTaskInputs;
316             _logInitialPropertiesAndItems = other._logInitialPropertiesAndItems;
317             _warningsAsErrors = other._warningsAsErrors == null ? null : new HashSet<string>(other._warningsAsErrors, StringComparer.OrdinalIgnoreCase);
318             _warningsAsMessages = other._warningsAsMessages == null ? null : new HashSet<string>(other._warningsAsMessages, StringComparer.OrdinalIgnoreCase);
319             _projectLoadSettings = other._projectLoadSettings;
320         }
321 
322 #if FEATURE_THREAD_PRIORITY
323         /// <summary>
324         /// Gets or sets the desired thread priority for building.
325         /// </summary>
326         public ThreadPriority BuildThreadPriority { get; set; } = ThreadPriority.Normal;
327 
328 #endif
329 
330         /// <summary>
331         /// 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
332         /// this property needs to be set to true.
333         /// </summary>
334         public bool UseSynchronousLogging
335         {
336             get => _useSynchronousLogging;
337             set => _useSynchronousLogging = value;
338         }
339 
340         /// <summary>
341         /// Gets the environment variables which were set when this build was created.
342         /// </summary>
343         public IDictionary<string, string> BuildProcessEnvironment => new ReadOnlyDictionary<string, string>(
344             _buildProcessEnvironment ?? new Dictionary<string, string>(0));
345 
346         /// <summary>
347         /// The name of the culture to use during the build.
348         /// </summary>
349         public CultureInfo Culture
350         {
351             get => _culture;
352             set => _culture = value;
353         }
354 
355         /// <summary>
356         /// The default tools version for the build.
357         /// </summary>
358         public string DefaultToolsVersion
359         {
360             get => _defaultToolsVersion;
361             set => _defaultToolsVersion = value;
362         }
363 
364         /// <summary>
365         /// When true, indicates that the build should emit a detailed summary at the end of the log.
366         /// </summary>
367         public bool DetailedSummary
368         {
369             get => _detailedSummary;
370             set => _detailedSummary = value;
371         }
372 
373         /// <summary>
374         /// When true, indicates the in-proc node should not be used.
375         /// </summary>
376         public bool DisableInProcNode
377         {
378             get => _disableInProcNode;
379             set => _disableInProcNode = value;
380         }
381 
382         /// <summary>
383         /// When true, indicates that the task parameters should be logged.
384         /// </summary>
385         public bool LogTaskInputs
386         {
387             get => _logTaskInputs;
388             set => _logTaskInputs = value;
389         }
390 
391         /// <summary>
392         /// When true, indicates that the initial properties and items should be logged.
393         /// </summary>
394         public bool LogInitialPropertiesAndItems
395         {
396             get => _logInitialPropertiesAndItems;
397             set => _logInitialPropertiesAndItems = value;
398         }
399 
400         /// <summary>
401         /// Indicates that the build should reset the configuration and results caches.
402         /// </summary>
403         public bool ResetCaches
404         {
405             get;
406             set;
407         }
408 
409         /// <summary>
410         /// Flag indicating whether out-of-proc nodes should remain after the build and wait for further builds.
411         /// </summary>
412         public bool EnableNodeReuse
413         {
414             get => _enableNodeReuse;
415             set => _enableNodeReuse = value;
416         }
417 
418         /// <summary>
419         /// Gets an immutable collection of environment properties.
420         /// </summary>
421         /// <remarks>
422         /// This differs from the BuildProcessEnvironment in that there are certain MSBuild-specific properties which are added, and those environment variables which
423         /// would not be valid as MSBuild properties are removed.
424         /// </remarks>
425         public IDictionary<string, string> EnvironmentProperties
426         {
427             get
428             {
429                 return new ReadOnlyConvertingDictionary<string, ProjectPropertyInstance, string>(_environmentProperties,
430                     instance => ((IProperty) instance).EvaluatedValueEscaped);
431             }
432         }
433 
434         /// <summary>
435         /// The collection of forwarding logger descriptions.
436         /// </summary>
437         public IEnumerable<ForwardingLoggerRecord> ForwardingLoggers
438         {
439             get => _forwardingLoggers;
440 
441             set
442             {
443                 if (value != null)
444                 {
445                     foreach (ForwardingLoggerRecord logger in value)
446                     {
447                         ErrorUtilities.VerifyThrowArgumentNull(logger, "ForwardingLoggers", "NullLoggerNotAllowed");
448                     }
449                 }
450 
451                 _forwardingLoggers = value;
452             }
453         }
454 
455         /// <summary>
456         /// Sets or retrieves an immutable collection of global properties.
457         /// </summary>
458         [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification =
459             "Accessor returns a readonly collection, and the BuildParameters class is immutable.")]
460         public IDictionary<string, string> GlobalProperties
461         {
462             get
463             {
464                 return new ReadOnlyConvertingDictionary<string, ProjectPropertyInstance, string>(_globalProperties,
465                     instance => ((IProperty) instance).EvaluatedValueEscaped);
466             }
467 
468             set
469             {
470                 _globalProperties = new PropertyDictionary<ProjectPropertyInstance>(value.Count);
471                 foreach (KeyValuePair<string, string> property in value)
472                 {
473                     _globalProperties[property.Key] = ProjectPropertyInstance.Create(property.Key, property.Value);
474                 }
475             }
476         }
477 
478         /// <summary>
479         /// Interface allowing the host to provide additional control over the build process.
480         /// </summary>
481         public HostServices HostServices
482         {
483             get => _hostServices;
484             set => _hostServices = value;
485         }
486 
487         /// <summary>
488         /// Enables or disables legacy threading semantics
489         /// </summary>
490         /// <remarks>
491         /// Legacy threading semantics indicate that if a submission is to be built
492         /// only on the in-proc node and the submission is executed synchronously, then all of its
493         /// requests will be built on the thread which invoked the build rather than a
494         /// thread owned by the BuildManager.
495         /// </remarks>
496         public bool LegacyThreadingSemantics
497         {
498             get;
499             set;
500         }
501 
502         /// <summary>
503         /// The collection of loggers to use during the build.
504         /// </summary>
505         public IEnumerable<ILogger> Loggers
506         {
507             get => _loggers;
508 
509             set
510             {
511                 if (value != null)
512                 {
513                     foreach (ILogger logger in value)
514                     {
515                         ErrorUtilities.VerifyThrowArgumentNull(logger, "Loggers", "NullLoggerNotAllowed");
516                     }
517                 }
518 
519                 _loggers = value;
520             }
521         }
522 
523         /// <summary>
524         /// The maximum number of nodes this build may use.
525         /// </summary>
526         public int MaxNodeCount
527         {
528             get => _maxNodeCount;
529 
530             set
531             {
532                 ErrorUtilities.VerifyThrowArgument(value > 0, "InvalidMaxNodeCount");
533                 _maxNodeCount = value;
534             }
535         }
536 
537         /// <summary>
538         /// The amount of memory the build should limit itself to using, in megabytes.
539         /// </summary>
540         public int MemoryUseLimit
541         {
542             get => _memoryUseLimit;
543             set => _memoryUseLimit = value;
544         }
545 
546         /// <summary>
547         /// The location of the build node executable.
548         /// </summary>
549         public string NodeExeLocation
550         {
551             get => _nodeExeLocation;
552             set => _nodeExeLocation = value;
553         }
554 
555         /// <summary>
556         /// Flag indicating if non-critical logging events should be discarded.
557         /// </summary>
558         public bool OnlyLogCriticalEvents
559         {
560             get => _onlyLogCriticalEvents;
561             set => _onlyLogCriticalEvents = value;
562         }
563 
564         /// <summary>
565         /// A list of warnings to treat as errors.  To treat all warnings as errors, set this to an empty <see cref="HashSet{String}"/>.
566         /// </summary>
567         public ISet<string> WarningsAsErrors
568         {
569             get => _warningsAsErrors;
570             set => _warningsAsErrors = value;
571         }
572 
573         /// <summary>
574         /// A list of warnings to treat as low importance messages.
575         /// </summary>
576         public ISet<string> WarningsAsMessages
577         {
578             get => _warningsAsMessages;
579             set => _warningsAsMessages = value;
580         }
581 
582         /// <summary>
583         /// Locations to search for toolsets.
584         /// </summary>
585         public ToolsetDefinitionLocations ToolsetDefinitionLocations
586         {
587             get => _toolsetDefinitionLocations;
588             set => _toolsetDefinitionLocations = value;
589         }
590 
591         /// <summary>
592         /// Returns all of the toolsets.
593         /// </summary>
594         /// <comments>
595         /// toolsetProvider.Toolsets is already a readonly collection.
596         /// </comments>
597         public ICollection<Toolset> Toolsets => _toolsetProvider.Toolsets;
598 
599         /// <summary>
600         /// The name of the UI culture to use during the build.
601         /// </summary>
602         public CultureInfo UICulture
603         {
604             get => _uiCulture;
605             set => _uiCulture = value;
606         }
607 
608         /// <summary>
609         /// Flag indicating if the operating environment such as the current directory and environment be saved and restored between project builds and task invocations.
610         /// 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.
611         /// </summary>
612         public bool SaveOperatingEnvironment
613         {
614             get => _saveOperatingEnvironment;
615             set => _saveOperatingEnvironment = value;
616         }
617 
618         /// <summary>
619         /// Shutdown the inprocess node when the build finishes. By default this is false
620         /// since visual studio needs to keep the inprocess node around after the build finishes.
621         /// </summary>
622         public bool ShutdownInProcNodeOnBuildFinish
623         {
624             get => _shutdownInProcNodeOnBuildFinish;
625             set => _shutdownInProcNodeOnBuildFinish = value;
626         }
627 
628         /// <summary>
629         /// Gets the internal msbuild thread stack size.
630         /// </summary>
631         internal static int ThreadStackSize => CommunicationsUtilities.GetIntegerVariableOrDefault(
632             "MSBUILDTHREADSTACKSIZE", DefaultThreadStackSize);
633 
634         /// <summary>
635         /// Gets the endpoint shutdown timeout.
636         /// </summary>
637         internal static int EndpointShutdownTimeout => CommunicationsUtilities.GetIntegerVariableOrDefault(
638             "MSBUILDENDPOINTSHUTDOWNTIMEOUT", DefaultEndpointShutdownTimeout);
639 
640         /// <summary>
641         /// Gets or sets the engine shutdown timeout.
642         /// </summary>
643         internal static int EngineShutdownTimeout => CommunicationsUtilities.GetIntegerVariableOrDefault(
644             "MSBUILDENGINESHUTDOWNTIMEOUT", DefaultEngineShutdownTimeout);
645 
646         /// <summary>
647         /// Gets the maximum number of idle request builders to retain.
648         /// </summary>
649         internal static int IdleRequestBuilderLimit => GetStaticIntVariableOrDefault("MSBUILDIDLEREQUESTBUILDERLIMIT",
650             ref s_idleRequestBuilderLimit, DefaultIdleRequestBuilderLimit);
651 
652         /// <summary>
653         /// Gets the logging thread shutdown timeout.
654         /// </summary>
655         internal static int LoggingThreadShutdownTimeout => CommunicationsUtilities.GetIntegerVariableOrDefault(
656             "MSBUILDLOGGINGTHREADSHUTDOWNTIMEOUT", DefaultLoggingThreadShutdownTimeout);
657 
658         /// <summary>
659         /// Gets the request builder shutdown timeout.
660         /// </summary>
661         internal static int RequestBuilderShutdownTimeout => CommunicationsUtilities.GetIntegerVariableOrDefault(
662             "MSBUILDREQUESTBUILDERSHUTDOWNTIMEOUT", DefaultRequestBuilderShutdownTimeout);
663 
664         /// <summary>
665         /// Gets the startup directory.
666         /// </summary>
667         internal static string StartupDirectory => s_startupDirectory;
668 
669         /// <summary>
670         /// Indicates whether the build plan is enabled or not.
671         /// </summary>
672         internal static bool EnableBuildPlan => GetStaticBoolVariableOrDefault("MSBUILDENABLEBUILDPLAN",
673             ref s_enableBuildPlan, false);
674 
675         /// <summary>
676         /// Indicates whether we should warn when a property is uninitialized when it is used.
677         /// </summary>
678         internal static bool WarnOnUninitializedProperty
679         {
680             get => GetStaticBoolVariableOrDefault("MSBUILDWARNONUNINITIALIZEDPROPERTY",
681                 ref s_warnOnUninitializedProperty, false);
682 
683             set => s_warnOnUninitializedProperty = value;
684         }
685 
686         /// <summary>
687         /// Indicates whether we should dump string interning stats
688         /// </summary>
689         internal static bool DumpOpportunisticInternStats => GetStaticBoolVariableOrDefault(
690             "MSBUILDDUMPOPPORTUNISTICINTERNSTATS", ref s_dumpOpportunisticInternStats, false);
691 
692         /// <summary>
693         /// Indicates whether we should dump debugging information about the expander
694         /// </summary>
695         internal static bool DebugExpansion => GetStaticBoolVariableOrDefault("MSBUILDDEBUGEXPANSION",
696             ref s_debugExpansion, false);
697 
698         /// <summary>
699         /// Indicates whether we should keep duplicate target outputs
700         /// </summary>
701         internal static bool KeepDuplicateOutputs => GetStaticBoolVariableOrDefault("MSBUILDKEEPDUPLICATEOUTPUTS",
702             ref s_keepDuplicateOutputs, false);
703 
704         /// <summary>
705         /// Gets or sets the build id.
706         /// </summary>
707         internal int BuildId
708         {
709             get => _buildId;
710             set => _buildId = value;
711         }
712 
713         /// <summary>
714         /// Gets or sets the environment properties.
715         /// </summary>
716         /// <remarks>
717         /// This is not the same as BuildProcessEnvironment.  See EnvironmentProperties.  These properties are those which
718         /// are used during evaluation of a project, and exclude those properties which would not be valid MSBuild properties
719         /// because they contain invalid characters (such as 'Program Files (x86)').
720         /// </remarks>
721         internal PropertyDictionary<ProjectPropertyInstance> EnvironmentPropertiesInternal
722         {
723             get => _environmentProperties;
724 
725             set
726             {
727                 ErrorUtilities.VerifyThrowInternalNull(value, "EnvironmentPropertiesInternal");
728                 _environmentProperties = value;
729             }
730         }
731 
732         /// <summary>
733         /// Gets the global properties.
734         /// </summary>
735         internal PropertyDictionary<ProjectPropertyInstance> GlobalPropertiesInternal => _globalProperties;
736 
737         /// <summary>
738         /// Gets or sets the node id.
739         /// </summary>
740         internal int NodeId { get; set; } = 0;
741 
742         /// <summary>
743         /// Gets the toolset provider.
744         /// </summary>
745         internal IToolsetProvider ToolsetProvider
746         {
747             get
748             {
749                 EnsureToolsets();
750                 return _toolsetProvider;
751             }
752         }
753 
754         /// <summary>
755         /// The one and only project root element cache to be used for the build.
756         /// </summary>
757         internal ProjectRootElementCache ProjectRootElementCache
758         {
759             get;
760             set;
761         }
762 
763 #if FEATURE_APPDOMAIN
764         /// <summary>
765         /// Information for configuring child AppDomains.
766         /// </summary>
767         internal AppDomainSetup AppDomainSetup
768         {
769             get;
770             set;
771         }
772 #endif
773 
774         /// <summary>
775         ///  (for diagnostic use) Whether or not this is out of proc
776         /// </summary>
777         internal bool IsOutOfProc
778         {
779             get;
780             set;
781         }
782 
783         /// <nodoc/>
784         public ProjectLoadSettings ProjectLoadSettings
785         {
786             get => _projectLoadSettings;
787             set => _projectLoadSettings = value;
788         }
789 
790 
791         /// <summary>
792         /// Retrieves a toolset.
793         /// </summary>
GetToolset(string toolsVersion)794         public Toolset GetToolset(string toolsVersion)
795         {
796             EnsureToolsets();
797             return _toolsetProvider.GetToolset(toolsVersion);
798         }
799 
800         /// <summary>
801         /// Creates a clone of this BuildParameters object.  This creates a clone of the logger collections, but does not deep clone
802         /// the loggers within.
803         /// </summary>
Clone()804         public BuildParameters Clone()
805         {
806             return new BuildParameters(this);
807         }
808 
809         /// <summary>
810         /// Implementation of the serialization mechanism.
811         /// </summary>
INodePacketTranslatable.Translate(INodePacketTranslator translator)812         void INodePacketTranslatable.Translate(INodePacketTranslator translator)
813         {
814             translator.Translate(ref _buildId);
815             /* No build thread priority during translation.  We specifically use the default (which is ThreadPriority.Normal) */
816             translator.TranslateDictionary(ref _buildProcessEnvironment, StringComparer.OrdinalIgnoreCase);
817             translator.TranslateCulture(ref _culture);
818             translator.Translate(ref _defaultToolsVersion);
819             translator.Translate(ref _disableInProcNode);
820             translator.Translate(ref _enableNodeReuse);
821             translator.TranslateProjectPropertyInstanceDictionary(ref _environmentProperties);
822             /* No forwarding logger information sent here - that goes with the node configuration */
823             translator.TranslateProjectPropertyInstanceDictionary(ref _globalProperties);
824             /* No host services during translation */
825             /* No loggers during translation */
826             translator.Translate(ref _maxNodeCount);
827             translator.Translate(ref _memoryUseLimit);
828             translator.Translate(ref _nodeExeLocation);
829             /* No node id during translation */
830             translator.Translate(ref _onlyLogCriticalEvents);
831             translator.Translate(ref s_startupDirectory);
832             translator.TranslateCulture(ref _uiCulture);
833             translator.Translate(ref _toolsetProvider, Microsoft.Build.Evaluation.ToolsetProvider.FactoryForDeserialization);
834             translator.Translate(ref _useSynchronousLogging);
835             translator.Translate(ref _shutdownInProcNodeOnBuildFinish);
836             translator.Translate(ref _logTaskInputs);
837             translator.Translate(ref _logInitialPropertiesAndItems);
838             translator.TranslateEnum(ref _projectLoadSettings, (int) _projectLoadSettings);
839 
840             // ProjectRootElementCache is not transmitted.
841             // ResetCaches is not transmitted.
842             // LegacyThreadingSemantics is not transmitted.
843         }
844 
845 #region INodePacketTranslatable Members
846 
847         /// <summary>
848         /// The class factory for deserialization.
849         /// </summary>
FactoryForDeserialization(INodePacketTranslator translator)850         internal static BuildParameters FactoryForDeserialization(INodePacketTranslator translator)
851         {
852             return new BuildParameters(translator);
853         }
854 
855 #endregion
856 
857         /// <summary>
858         /// Gets the value of a boolean environment setting which is not expected to change.
859         /// </summary>
GetStaticBoolVariableOrDefault(string environmentVariable, ref bool? backing, bool @default)860         private static bool GetStaticBoolVariableOrDefault(string environmentVariable, ref bool? backing, bool @default)
861         {
862             if (!backing.HasValue)
863             {
864                 backing = !String.IsNullOrEmpty(Environment.GetEnvironmentVariable(environmentVariable)) || @default;
865             }
866 
867             return backing.Value;
868         }
869 
870         /// <summary>
871         /// Gets the value of an integer environment variable, or returns the default if none is set or it cannot be converted.
872         /// </summary>
GetStaticIntVariableOrDefault(string environmentVariable, ref int? backingValue, int defaultValue)873         private static int GetStaticIntVariableOrDefault(string environmentVariable, ref int? backingValue, int defaultValue)
874         {
875             if (!backingValue.HasValue)
876             {
877                 string environmentValue = Environment.GetEnvironmentVariable(environmentVariable);
878                 if (String.IsNullOrEmpty(environmentValue))
879                 {
880                     backingValue = defaultValue;
881                 }
882                 else
883                 {
884                     backingValue = Int32.TryParse(environmentValue, out var parsedValue) ? parsedValue : defaultValue;
885                 }
886             }
887 
888             return backingValue.Value;
889         }
890 
891         /// <summary>
892         /// Centralization of the common parts of construction.
893         /// </summary>
Initialize(PropertyDictionary<ProjectPropertyInstance> environmentProperties, ProjectRootElementCache projectRootElementCache, ToolsetProvider toolsetProvider)894         private void Initialize(PropertyDictionary<ProjectPropertyInstance> environmentProperties, ProjectRootElementCache projectRootElementCache, ToolsetProvider toolsetProvider)
895         {
896             _buildProcessEnvironment = CommunicationsUtilities.GetEnvironmentVariables();
897             _environmentProperties = environmentProperties;
898             ProjectRootElementCache = projectRootElementCache;
899             ResetCaches = true;
900             _toolsetProvider = toolsetProvider;
901 
902             if (Environment.GetEnvironmentVariable("MSBUILDDISABLENODEREUSE") == "1") // For example to disable node reuse within Visual Studio
903             {
904                 _enableNodeReuse = false;
905             }
906 
907             if (Environment.GetEnvironmentVariable("MSBUILDDETAILEDSUMMARY") == "1") // For example to get detailed summary within Visual Studio
908             {
909                 _detailedSummary = true;
910             }
911 
912             _nodeExeLocation = FindMSBuildExe();
913         }
914 
915         /// <summary>
916         /// Loads the toolsets if we don't have them already.
917         /// </summary>
EnsureToolsets()918         private void EnsureToolsets()
919         {
920             if (_toolsetProvider != null)
921             {
922                 return;
923             }
924 
925             _toolsetProvider = new ToolsetProvider(DefaultToolsVersion, _environmentProperties, _globalProperties, ToolsetDefinitionLocations);
926         }
927 
928         /// <summary>
929         /// This method determines where MSBuild.Exe is and sets the NodeExePath to that by default.
930         /// </summary>
FindMSBuildExe()931         private string FindMSBuildExe()
932         {
933             string location = _nodeExeLocation;
934 
935             // Use the location specified by the user in code.
936             if (!string.IsNullOrEmpty(location) && CheckMSBuildExeExistsAt(location))
937             {
938                 return location;
939             }
940 
941             // Try what we think is the current executable path.
942             return BuildEnvironmentHelper.Instance.CurrentMSBuildExePath;
943         }
944 
945         /// <summary>
946         /// Helper to avoid doing an expensive disk check for MSBuild.exe when
947         /// we already checked in a previous build.
948         /// This File.Exists otherwise can show up in profiles when there's a lot of
949         /// design time builds going on.
950         /// </summary>
CheckMSBuildExeExistsAt(string path)951         private bool CheckMSBuildExeExistsAt(string path)
952         {
953             if (s_msbuildExeKnownToExistAt != null && string.Equals(path, s_msbuildExeKnownToExistAt, StringComparison.OrdinalIgnoreCase))
954             {
955                 // We found it there last time: it must exist there.
956                 return true;
957             }
958 
959             if (File.Exists(path))
960             {
961                 s_msbuildExeKnownToExistAt = path;
962                 return true;
963             }
964 
965             return false;
966         }
967     }
968 }
969