-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathChangeWaves.cs
More file actions
217 lines (195 loc) · 7.05 KB
/
ChangeWaves.cs
File metadata and controls
217 lines (195 loc) · 7.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
#if DEBUG
using System.Diagnostics;
#endif
using System.Linq;
#nullable disable
namespace Microsoft.Build.Framework
{
internal enum ChangeWaveConversionState
{
NotConvertedYet,
Valid,
InvalidFormat,
OutOfRotation
}
/// <summary>
/// Coupled together with the MSBUILDDISABLEFEATURESFROMVERSION environment variable,
/// this class acts as a way to make risky changes while giving customers an opt-out.
/// </summary>
/// See docs here: https://github.com/dotnet/msbuild/blob/main/documentation/wiki/ChangeWaves.md
/// For dev docs: https://github.com/dotnet/msbuild/blob/main/documentation/wiki/ChangeWaves-Dev.md
internal static class ChangeWaves
{
internal static readonly Version Wave17_10 = new Version(17, 10);
internal static readonly Version Wave17_12 = new Version(17, 12);
internal static readonly Version Wave17_14 = new Version(17, 14);
internal static readonly Version Wave18_3 = new Version(18, 3);
internal static readonly Version Wave18_4 = new Version(18, 4);
internal static readonly Version Wave18_5 = new Version(18, 5);
internal static readonly Version Wave18_6 = new Version(18, 6);
internal static readonly Version Wave18_7 = new Version(18, 7);
internal static readonly Version[] AllWaves = [Wave17_10, Wave17_12, Wave17_14, Wave18_3, Wave18_4, Wave18_5, Wave18_6, Wave18_7];
/// <summary>
/// Special value indicating that all features behind all Change Waves should be enabled.
/// </summary>
internal static readonly Version EnableAllFeatures = new Version(999, 999);
#if DEBUG
/// <summary>
/// True if <see cref="ResetStateForTests"/> has been called.
/// </summary>
private static bool _runningTests = false;
#endif
/// <summary>
/// The lowest wave in the current rotation of Change Waves.
/// </summary>
internal static Version LowestWave
{
get
{
return AllWaves[0];
}
}
/// <summary>
/// The highest wave in the current rotation of Change Waves.
/// </summary>
internal static Version HighestWave
{
get
{
return AllWaves[AllWaves.Length - 1];
}
}
/// <summary>
/// Checks the conditions for whether or not we want ApplyChangeWave to be called again.
/// </summary>
private static bool ShouldApplyChangeWave
{
get
{
return ConversionState == ChangeWaveConversionState.NotConvertedYet || _cachedWave == null;
}
}
private static Version _cachedWave;
/// <summary>
/// The current disabled wave.
/// </summary>
internal static Version DisabledWave
{
get
{
if (ShouldApplyChangeWave)
{
ApplyChangeWave();
}
return _cachedWave;
}
}
private static ChangeWaveConversionState _state;
/// <summary>
/// The status of how the disabled wave was set.
/// </summary>
internal static ChangeWaveConversionState ConversionState
{
get
{
return _state;
}
set
{
// Keep state persistent.
if (_state == ChangeWaveConversionState.NotConvertedYet)
{
_state = value;
}
}
}
/// <summary>
/// Read from environment variable `MSBUILDDISABLEFEATURESFROMVERSION`, correct it if required, cache it and its ConversionState.
/// </summary>
internal static void ApplyChangeWave()
{
// Once set, change wave should not need to be set again.
if (!ShouldApplyChangeWave)
{
return;
}
string msbuildDisableFeaturesFromVersion = Environment.GetEnvironmentVariable("MSBUILDDISABLEFEATURESFROMVERSION");
// Most common case, `MSBUILDDISABLEFEATURESFROMVERSION` unset
if (string.IsNullOrEmpty(msbuildDisableFeaturesFromVersion))
{
ConversionState = ChangeWaveConversionState.Valid;
_cachedWave = ChangeWaves.EnableAllFeatures;
}
else if (!TryParseVersion(msbuildDisableFeaturesFromVersion, out _cachedWave))
{
ConversionState = ChangeWaveConversionState.InvalidFormat;
_cachedWave = ChangeWaves.EnableAllFeatures;
}
else if (_cachedWave == EnableAllFeatures || Array.IndexOf(AllWaves, _cachedWave) >= 0)
{
ConversionState = ChangeWaveConversionState.Valid;
}
else if (_cachedWave < LowestWave)
{
ConversionState = ChangeWaveConversionState.OutOfRotation;
_cachedWave = LowestWave;
}
else if (_cachedWave > HighestWave)
{
ConversionState = ChangeWaveConversionState.OutOfRotation;
_cachedWave = HighestWave;
}
// _cachedWave is somewhere between valid waves, find the next valid version.
else
{
_cachedWave = AllWaves.First((x) => x > _cachedWave);
ConversionState = ChangeWaveConversionState.Valid;
}
}
/// <summary>
/// Determines whether features behind the given wave are enabled.
/// </summary>
/// <param name="wave">The version to compare.</param>
/// <returns>A bool indicating whether the change wave is enabled.</returns>
internal static bool AreFeaturesEnabled(Version wave)
{
ApplyChangeWave();
#if DEBUG
Debug.Assert(_runningTests || Array.IndexOf(AllWaves, wave) >= 0, $"Change wave version {wave} is invalid");
#endif
return wave < _cachedWave;
}
/// <summary>
/// Resets the state and value of the currently disabled version.
/// Used for testing only.
/// </summary>
internal static void ResetStateForTests()
{
#if DEBUG
_runningTests = true;
#endif
_cachedWave = null;
_state = ChangeWaveConversionState.NotConvertedYet;
}
private static bool TryParseVersion(string stringVersion, out Version version)
{
#if FEATURE_NET35_TASKHOST
try
{
version = new Version(stringVersion);
return true;
}
catch (Exception)
{
version = null;
return false;
}
#else
return Version.TryParse(stringVersion, out version);
#endif
}
}
}