Skip to content

Commit c5d79a6

Browse files
authored
Merge pull request #95 from sfoslund/VSVersionProtection
Vs version protection
2 parents 6179855 + 2004f13 commit c5d79a6

25 files changed

+314
-61
lines changed

src/dotnet-core-uninstall/LocalizableStrings.Designer.cs

Lines changed: 59 additions & 38 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/dotnet-core-uninstall/LocalizableStrings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,4 +294,7 @@ Do you want to continue? [Y/n] </value>
294294
<data name="ConfirmationPromptInvalidExceptionMessage" xml:space="preserve">
295295
<value>Allowed values are "Y" and "n".</value>
296296
</data>
297+
<data name="ListIncludeRequiredOptionDescription" xml:space="preserve">
298+
<value>Include .NET Core SDKs that cannot be uninstalled.</value>
299+
</data>
297300
</root>

src/dotnet-core-uninstall/MacOs/FileSystemExplorer.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Linq;
55
using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo;
66
using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo.Versioning;
7+
using Microsoft.DotNet.Tools.Uninstall.Shared.VSVersioning;
78

89
namespace Microsoft.DotNet.Tools.Uninstall.MacOs
910
{
@@ -17,6 +18,11 @@ internal static class FileSystemExplorer
1718
private static readonly string DotNetHostFxrInstallPath = Path.Combine(DotNetInstallPath, "host", "fxr");
1819

1920
public static IEnumerable<Bundle> GetInstalledBundles()
21+
{
22+
return VisualStudioSafeVersionsExtractor.GetUninstallableBundles(GetAllInstalledBundles());
23+
}
24+
25+
public static IEnumerable<Bundle> GetAllInstalledBundles()
2026
{
2127
var sdks = GetInstalledBundles<SdkVersion>(DotNetSdkInstallPath);
2228
var runtimes = GetInstalledBundles<RuntimeVersion>(
@@ -25,7 +31,7 @@ public static IEnumerable<Bundle> GetInstalledBundles()
2531
DotNetAspAppInstallPath,
2632
DotNetHostFxrInstallPath);
2733

28-
return sdks.Concat(runtimes);
34+
return sdks.Concat(runtimes).ToList();
2935
}
3036

3137
private static IEnumerable<Bundle> GetInstalledBundles<TBundleVersion>(params string[] paths)

src/dotnet-core-uninstall/MacOs/SupportedBundleTypeConfigs.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,19 @@ namespace Microsoft.DotNet.Tools.Uninstall.MacOs
1010
{
1111
internal static class SupportedBundleTypeConfigs
1212
{
13-
private static readonly Func<IList<Bundle>, GridView> _gridViewGeneratorWithArch = bundles =>
13+
private static readonly Func<IDictionary<Bundle, string>, GridView> _gridViewGeneratorWithArch = bundles =>
1414
{
1515
var gridView = new GridView();
1616

17-
gridView.SetColumns(Enumerable.Repeat(ColumnDefinition.SizeToContent(), 3).ToArray());
17+
gridView.SetColumns(Enumerable.Repeat(ColumnDefinition.SizeToContent(), 4).ToArray());
1818
gridView.SetRows(Enumerable.Repeat(RowDefinition.SizeToContent(), Math.Max(bundles.Count, 1)).ToArray());
1919

2020
foreach (var (bundle, index) in bundles.Select((bundle, index) => (bundle, index)))
2121
{
2222
gridView.SetChild(new ContentView(string.Empty), 0, index);
23-
gridView.SetChild(new ContentView(bundle.Version.ToString()), 1, index);
24-
gridView.SetChild(new ContentView($"({bundle.Arch.ToString().ToLower()})"), 2, index);
23+
gridView.SetChild(new ContentView(bundle.Key.Version.ToString()), 1, index);
24+
gridView.SetChild(new ContentView($"({bundle.Key.Arch.ToString().ToLower()})"), 2, index);
25+
gridView.SetChild(new ContentView(bundle.Value), 3, index);
2526
}
2627

2728
return gridView;

src/dotnet-core-uninstall/Shared/BundleInfo/Versioning/BundleVersion.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ internal abstract class BundleVersion
1212
public string Footnote { get; private set; }
1313
public bool HasFootnote => Footnote != null;
1414

15-
protected SemanticVersion SemVer { get; private set; }
15+
public SemanticVersion SemVer { get; private set; }
1616

1717
public virtual int Major => SemVer.Major;
1818
public virtual int Minor => SemVer.Minor;

src/dotnet-core-uninstall/Shared/Commands/ListCommandExec.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Microsoft.DotNet.Tools.Uninstall.Shared.Configs;
1010
using Microsoft.DotNet.Tools.Uninstall.Shared.Exceptions;
1111
using Microsoft.DotNet.Tools.Uninstall.Shared.Utils;
12+
using Microsoft.DotNet.Tools.Uninstall.Shared.VSVersioning;
1213
using Microsoft.DotNet.Tools.Uninstall.Windows;
1314

1415
namespace Microsoft.DotNet.Tools.Uninstall.Shared.Commands
@@ -20,13 +21,13 @@ public static void Execute()
2021
if (RuntimeInfo.RunningOnWindows)
2122
{
2223
Execute(
23-
RegistryQuery.GetInstalledBundles(),
24+
RegistryQuery.GetAllInstalledBundles(),
2425
Windows.SupportedBundleTypeConfigs.SupportedBundleTypes);
2526
}
2627
else if (RuntimeInfo.RunningOnOSX)
2728
{
2829
Execute(
29-
FileSystemExplorer.GetInstalledBundles(),
30+
FileSystemExplorer.GetAllInstalledBundles(),
3031
MacOs.SupportedBundleTypeConfigs.SupportedBundleTypes);
3132
}
3233
else
@@ -41,13 +42,16 @@ private static void Execute(
4142
{
4243
var listCommandParseResult = CommandLineConfigs.ListCommand.Parse(Environment.GetCommandLineArgs());
4344

45+
var uninstallableBundles = VisualStudioSafeVersionsExtractor.GetUninstallableBundles(bundles);
46+
4447
var typeSelection = listCommandParseResult.CommandResult.GetTypeSelection();
4548
var archSelection = listCommandParseResult.CommandResult.GetArchSelection();
4649

4750
var stackView = new StackLayoutView();
4851

4952
var filteredBundlesByArch = bundles.Where(bundle => archSelection.HasFlag(bundle.Arch));
5053

54+
5155
var footnotes = new List<string>();
5256

5357
foreach (var bundleType in supportedBundleTypes)
@@ -58,8 +62,14 @@ private static void Execute(
5862
.Filter(filteredBundlesByArch)
5963
.OrderByDescending(bundle => bundle);
6064

65+
var uninstallMap = filteredBundlesByType
66+
.Select(bundle => uninstallableBundles.Contains(bundle) ?
67+
new KeyValuePair<Bundle, string>(bundle, string.Empty) :
68+
new KeyValuePair<Bundle, string>(bundle, "[Not Uninstallable]"))
69+
.ToDictionary(i => i.Key, i => i.Value);
70+
6171
stackView.Add(new ContentView(bundleType.Header));
62-
stackView.Add(bundleType.GridViewGenerator.Invoke(filteredBundlesByType.ToArray()));
72+
stackView.Add(bundleType.GridViewGenerator.Invoke(uninstallMap));
6373
stackView.Add(new ContentView(string.Empty));
6474

6575
footnotes.AddRange(filteredBundlesByType

src/dotnet-core-uninstall/Shared/Configs/BundleTypePrintInfo.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ internal abstract class BundleTypePrintInfo
1111
public abstract BundleType Type { get; }
1212

1313
public string Header { get; }
14-
public Func<IList<Bundle>, GridView> GridViewGenerator { get; }
14+
public Func<IDictionary<Bundle, string>, GridView> GridViewGenerator { get; }
1515
public string OptionName { get; }
1616

17-
protected BundleTypePrintInfo(string header, Func<IList<Bundle>, GridView> gridViewGenerator, string optionName)
17+
protected BundleTypePrintInfo(string header, Func<IDictionary<Bundle, string>, GridView> gridViewGenerator, string optionName)
1818
{
1919
Header = header ?? throw new ArgumentNullException();
2020
GridViewGenerator = gridViewGenerator ?? throw new ArgumentNullException();
@@ -29,7 +29,7 @@ internal class BundleTypePrintInfo<TBundleVersion> : BundleTypePrintInfo
2929
{
3030
public override BundleType Type => new TBundleVersion().Type;
3131

32-
public BundleTypePrintInfo(string header, Func<IList<Bundle>, GridView> gridViewGenerator, string optionName) :
32+
public BundleTypePrintInfo(string header, Func<IDictionary<Bundle, string>, GridView> gridViewGenerator, string optionName) :
3333
base(header, gridViewGenerator, optionName)
3434
{ }
3535

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo;
4+
using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo.Versioning;
5+
using NuGet.Versioning;
6+
7+
namespace Microsoft.DotNet.Tools.Uninstall.Shared.VSVersioning
8+
{
9+
// Visual Studio versions 15.3 thru 15.9 (inclusive) requires an sdk with max version 2.1.5xx (inclusive)
10+
// 16.0 requires anything less than 3.0.1xx
11+
// Must keep one of each Major.Minor band to ensure runtime works
12+
internal static class VisualStudioSafeVersionsExtractor
13+
{
14+
// Divisions within version bands. Not inclusive, groupings: [2.1.000, 2.1.600) [2.1.600, 2.2.00) [2.0.00, 2.2.200) [2.2.200, 2.3.00)
15+
private static readonly SemanticVersion[] SpecialCaseDivisions = { new SemanticVersion(2, 1, 600), new SemanticVersion(2, 2, 200) };
16+
17+
public static IEnumerable<Bundle> GetUninstallableBundles(IEnumerable<Bundle> bundles)
18+
{
19+
var required = new List<Bundle>();
20+
var bundlesByBand = SortSdkBundlesByVersionBand(bundles
21+
.Where(b => b.Version is SdkVersion)
22+
.Select(b => b as Bundle<SdkVersion>));
23+
24+
foreach (IEnumerable<Bundle> band in bundlesByBand)
25+
{
26+
required.Add(band.Max());
27+
}
28+
29+
return bundles.Where(b => !required.Contains(b));
30+
}
31+
32+
private static IEnumerable<IEnumerable<Bundle>> SortSdkBundlesByVersionBand(IEnumerable<Bundle<SdkVersion>> bundles)
33+
{
34+
var sortedBundles = new List<IEnumerable<Bundle>>() as IEnumerable<IEnumerable<Bundle>>;
35+
var majorMinorGroups = bundles.GroupBy(bundle => bundle.Version.MajorMinor);
36+
foreach (SemanticVersion specialCase in SpecialCaseDivisions)
37+
{
38+
sortedBundles = sortedBundles.Concat(
39+
majorMinorGroups.Select(bundleList => DivideSpecialCases(bundleList, specialCase))
40+
.SelectMany(lst => lst));
41+
}
42+
return sortedBundles;
43+
}
44+
45+
private static IEnumerable<IEnumerable<Bundle>> DivideSpecialCases(IEnumerable<Bundle> bundleList, SemanticVersion division)
46+
{
47+
return bundleList.FirstOrDefault().Version.MajorMinor.Equals(new MajorMinorVersion(division.Major, division.Minor)) ?
48+
bundleList.GroupBy(bundle => bundle.Version.SemVer.Patch < division.Patch) as IEnumerable<IEnumerable<Bundle>> :
49+
new List<IEnumerable<Bundle>> { bundleList };
50+
}
51+
}
52+
}

src/dotnet-core-uninstall/Windows/RegistryQuery.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,19 @@
55
using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo;
66
using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo.Versioning;
77
using Microsoft.DotNet.Tools.Uninstall.Shared.Utils;
8+
using Microsoft.DotNet.Tools.Uninstall.Shared.VSVersioning;
89
using Microsoft.Win32;
910

1011
namespace Microsoft.DotNet.Tools.Uninstall.Windows
1112
{
1213
internal static class RegistryQuery
1314
{
1415
public static IEnumerable<Bundle> GetInstalledBundles()
16+
{
17+
return VisualStudioSafeVersionsExtractor.GetUninstallableBundles(GetAllInstalledBundles());
18+
}
19+
20+
public static IEnumerable<Bundle> GetAllInstalledBundles()
1521
{
1622
var uninstalls = Registry.LocalMachine
1723
.OpenSubKey("SOFTWARE");
@@ -33,8 +39,10 @@ public static IEnumerable<Bundle> GetInstalledBundles()
3339
.Select(name => uninstalls.OpenSubKey(name))
3440
.Where(bundle => IsDotNetCoreBundle(bundle));
3541

36-
return bundles
37-
.Select(bundle => WrapRegistryKey(bundle));
42+
var wrappedBundles = bundles
43+
.Select(bundle => WrapRegistryKey(bundle)).ToList();
44+
45+
return wrappedBundles;
3846
}
3947

4048
private static bool IsDotNetCoreBundle(RegistryKey registryKey)

0 commit comments

Comments
 (0)