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 
6 using System;
7 using Microsoft.Build.Shared;
8 
9 namespace Microsoft.Build.Utilities
10 {
11     /// <summary>
12     ///     Represents toggleable features of the MSBuild engine
13     /// </summary>
14     internal class Traits
15     {
16         private static Traits _instance = new Traits();
17         public static Traits Instance
18         {
19             get
20             {
21                 if (BuildEnvironmentHelper.Instance.RunningTests && Environment.GetEnvironmentVariable("MSBUILDRELOADTRAITSONEACHACCESS") == "1")
22                 {
23                     return new Traits();
24                 }
25                 return _instance;
26             }
27         }
28 
29         public EscapeHatches EscapeHatches { get; }
30 
31         /// <summary>
32         /// Do not expand wildcards that match a certain pattern
33         /// </summary>
34         public readonly bool UseLazyWildCardEvaluation = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MsBuildSkipEagerWildCardEvaluationRegexes"));
35         public readonly bool LogExpandedWildcards = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDLOGEXPANDEDWILDCARDS"));
36         public readonly bool CacheFileExistence = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MsBuildCacheFileExistence"));
37 
38         /// <summary>
39         /// Eliminate locking in OpportunisticIntern at the expense of memory
40         /// </summary>
41         public readonly bool UseSimpleInternConcurrency = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MsBuildUseSimpleInternConcurrency"));
42 
43         /// <summary>
44         /// Cache wildcard expansions
45         /// </summary>
46         public readonly bool MSBuildCacheFileEnumerations = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MsBuildCacheFileEnumerations"));
47 
Traits()48         public Traits()
49         {
50             EscapeHatches = new EscapeHatches();
51         }
52 
53         public readonly bool EnableAllPropertyFunctions = Environment.GetEnvironmentVariable("MSBUILDENABLEALLPROPERTYFUNCTIONS") == "1";
54 
55         /// <summary>
56         /// Enable restore first functionality in MSBuild.exe
57         /// </summary>
58         public readonly bool EnableRestoreFirst = Environment.GetEnvironmentVariable("MSBUILDENABLERESTOREFIRST") == "1";
59     }
60 
61     internal class EscapeHatches
62     {
63         /// <summary>
64         /// Force whether Project based evaluations should evaluate elements with false conditions.
65         /// </summary>
66         public readonly bool? EvaluateElementsWithFalseConditionInProjectEvaluation = ParseNullableBoolFromEnvironmentVariable("MSBUILDEVALUATEELEMENTSWITHFALSECONDITIONINPROJECTEVALUATION");
67 
68         /// <summary>
69         /// Always use the accurate-but-slow CreateFile approach to timestamp extraction.
70         /// </summary>
71         public readonly bool AlwaysUseContentTimestamp = Environment.GetEnvironmentVariable("MSBUILDALWAYSCHECKCONTENTTIMESTAMP") == "1";
72 
73         public readonly bool LogProjectImports = Environment.GetEnvironmentVariable("MSBUILDLOGIMPORTS") == "1";
74 
75         /// <summary>
76         /// Read information only once per file per ResolveAssemblyReference invocation.
77         /// </summary>
78         public readonly bool CacheAssemblyInformation = Environment.GetEnvironmentVariable("MSBUILDDONOTCACHERARASSEMBLYINFORMATION") != "1";
79 
80         public readonly ProjectInstanceTranslationMode? ProjectInstanceTranslation = ComputeProjectInstanceTranslation();
81 
82         /// <summary>
83         /// Never use the slow (but more accurate) CreateFile approach to timestamp extraction.
84         /// </summary>
85         public readonly bool UseSymlinkTimeInsteadOfTargetTime = Environment.GetEnvironmentVariable("MSBUILDUSESYMLINKTIMESTAMP") == "1";
86 
87         /// <summary>
88         /// Whether or not to ignore imports that are considered empty.  See ProjectRootElement.IsEmptyXmlFile() for more info.
89         /// </summary>
90         public readonly bool IgnoreEmptyImports = Environment.GetEnvironmentVariable("MSBUILDIGNOREEMPTYIMPORTS") == "1";
91 
92         /// <summary>
93         /// Whether to to respect the TreatAsLocalProperty parameter on the Project tag.
94         /// </summary>
95         public readonly bool IgnoreTreatAsLocalProperty = Environment.GetEnvironmentVariable("MSBUILDIGNORETREATASLOCALPROPERTY") != null;
96 
97         /// <summary>
98         /// Whether to write information about why we evaluate to debug output.
99         /// </summary>
100         public readonly bool DebugEvaluation = Environment.GetEnvironmentVariable("MSBUILDDEBUGEVALUATION") != null;
101 
102         /// <summary>
103         /// Whether to warn when we set a property for the first time, after it was previously used.
104         /// </summary>
105         public readonly bool WarnOnUninitializedProperty = !String.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDWARNONUNINITIALIZEDPROPERTY"));
106 
107         // MSBUILDUSECASESENSITIVEITEMNAMES is an escape hatch for the fix
108         // for https://github.com/Microsoft/msbuild/issues/1751. It should
109         // be removed (permanently set to false) after establishing that
110         // it's unneeded (at least by the 16.0 timeframe).
111         public readonly bool UseCaseSensitiveItemNames = Environment.GetEnvironmentVariable("MSBUILDUSECASESENSITIVEITEMNAMES") == "1";
112 
113         /// <summary>
114         /// Disable the use of any caching when resolving SDKs.
115         /// </summary>
116         public readonly bool DisableSdkResolutionCache = Environment.GetEnvironmentVariable("MSBUILDDISABLESDKCACHE") == "1";
117 
118         /// <summary>
119         /// Disable the NuGet-based SDK resolver.
120         /// </summary>
121         public readonly bool DisableNuGetSdkResolver = Environment.GetEnvironmentVariable("MSBUILDDISABLENUGETSDKRESOLVER") == "1";
122 
123         /// <summary>
124         /// Enables the user of autorun functionality in CMD.exe on Windows which is disabled by default in MSBuild.
125         /// </summary>
126         public readonly bool UseAutoRunWhenLaunchingProcessUnderCmd = Environment.GetEnvironmentVariable("MSBUILDUSERAUTORUNINCMD") == "1";
127 
128         /// <summary>
129         /// Workaround for https://github.com/Microsoft/vstest/issues/1503.
130         /// </summary>
131         public readonly bool EnsureStdOutForChildNodesIsPrimaryStdout = Environment.GetEnvironmentVariable("MSBUILDENSURESTDOUTFORTASKPROCESSES") == "1";
132 
133 
ParseNullableBoolFromEnvironmentVariable(string environmentVariable)134         private static bool? ParseNullableBoolFromEnvironmentVariable(string environmentVariable)
135         {
136             var value = Environment.GetEnvironmentVariable(environmentVariable);
137 
138             if (string.IsNullOrEmpty(value))
139             {
140                 return null;
141             }
142 
143             bool result;
144             if (bool.TryParse(value, out result))
145             {
146                 return result;
147             }
148 
149             ErrorUtilities.ThrowInternalError($"Environment variable \"{environmentVariable}\" should have values \"true\", \"false\" or undefined");
150 
151             return null;
152         }
153 
ComputeProjectInstanceTranslation()154         private static ProjectInstanceTranslationMode? ComputeProjectInstanceTranslation()
155         {
156             var mode = Environment.GetEnvironmentVariable("MSBUILD_PROJECTINSTANCE_TRANSLATION_MODE");
157 
158             if (mode == null)
159             {
160                 return null;
161             }
162 
163             if (mode.Equals("full", StringComparison.OrdinalIgnoreCase))
164             {
165                 return ProjectInstanceTranslationMode.Full;
166             }
167 
168             if (mode.Equals("partial", StringComparison.OrdinalIgnoreCase))
169             {
170                 return ProjectInstanceTranslationMode.Partial;
171             }
172 
173             ErrorUtilities.ThrowInternalError($"Invalid escape hatch for project instance translation: {mode}");
174 
175             return null;
176         }
177 
178         public enum ProjectInstanceTranslationMode
179         {
180             Full,
181             Partial
182         }
183     }
184 }
185