diff --git a/src/Cli/dotnet/commands/dotnet-workload/InstallType.cs b/src/Cli/dotnet/commands/dotnet-workload/InstallType.cs
deleted file mode 100644
index c89d0f6f4937..000000000000
--- a/src/Cli/dotnet/commands/dotnet-workload/InstallType.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-namespace Microsoft.DotNet.Workloads.Workload
-{
- ///
- /// Describes different workload installation types.
- ///
- internal enum InstallType
- {
- ///
- /// Workloads are installed as NuGet packages
- ///
- FileBased = 0,
- ///
- /// Workloads are installed as MSIs.
- ///
- Msi = 1
- }
-}
diff --git a/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandParser.cs b/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandParser.cs
index aea59e789d2b..7a49de4f4f05 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandParser.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandParser.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Parsing;
@@ -41,14 +42,12 @@ internal static void ShowWorkloadsInfo(IWorkloadInfoHelper workloadInfoHelper =
return;
}
+ var manifestInfoDict = workloadInfoHelper.WorkloadResolver.GetInstalledManifests().ToDictionary(info => info.Id, StringComparer.OrdinalIgnoreCase);
foreach (var workload in installedWorkloads.AsEnumerable())
{
var workloadManifest = workloadInfoHelper.WorkloadResolver.GetManifestFromWorkload(new WorkloadId(workload.Key));
- var workloadFeatureBand = new WorkloadManifestInfo(
- workloadManifest.Id,
- workloadManifest.Version,
- Path.GetDirectoryName(workloadManifest.ManifestPath)!).ManifestFeatureBand;
+ var workloadFeatureBand = manifestInfoDict[workloadManifest.Id].ManifestFeatureBand;
const int align = 10;
const string separator = " ";
@@ -65,7 +64,7 @@ internal static void ShowWorkloadsInfo(IWorkloadInfoHelper workloadInfoHelper =
reporter.WriteLine($" {workloadManifest.ManifestPath,align}");
reporter.Write($"{separator}{CommonStrings.WorkloadInstallTypeColumn}:");
- reporter.WriteLine($" {WorkloadInstallerFactory.GetWorkloadInstallType(new SdkFeatureBand(workloadFeatureBand), workloadManifest.ManifestPath).ToString(),align}"
+ reporter.WriteLine($" {WorkloadInstallType.GetWorkloadInstallType(new SdkFeatureBand(workloadFeatureBand), workloadManifest.ManifestPath).ToString(),align}"
);
reporter.WriteLine("");
}
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallerFactory.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallerFactory.cs
index 117fa4e342e9..16d45439a117 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallerFactory.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallerFactory.cs
@@ -29,7 +29,7 @@ public static IInstaller GetWorkloadInstaller(
bool elevationRequired = true)
{
dotnetDir = string.IsNullOrWhiteSpace(dotnetDir) ? Path.GetDirectoryName(Environment.ProcessPath) : dotnetDir;
- var installType = GetWorkloadInstallType(sdkFeatureBand, dotnetDir);
+ var installType = WorkloadInstallType.GetWorkloadInstallType(sdkFeatureBand, dotnetDir);
if (installType == InstallType.Msi)
{
@@ -61,24 +61,6 @@ public static IInstaller GetWorkloadInstaller(
restoreActionConfig: restoreActionConfig);
}
- ///
- /// Determines the associated with a specific SDK version.
- ///
- /// The SDK version to check.
- /// The associated with the SDK.
- public static InstallType GetWorkloadInstallType(SdkFeatureBand sdkFeatureBand, string dotnetDir)
- {
- string installerTypePath = Path.Combine(dotnetDir, "metadata",
- "workloads", $"{sdkFeatureBand.ToStringWithoutPrerelease()}", "installertype");
-
- if (File.Exists(Path.Combine(installerTypePath, "msi")))
- {
- return InstallType.Msi;
- }
-
- return InstallType.FileBased;
- }
-
private static bool CanWriteToDotnetRoot(string dotnetDir = null)
{
dotnetDir = dotnetDir ?? Path.GetDirectoryName(Environment.ProcessPath);
diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/WorkloadListCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/list/WorkloadListCommand.cs
index 298a54f52dfe..9581b7664c5e 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/list/WorkloadListCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/list/WorkloadListCommand.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Parsing;
@@ -79,6 +80,8 @@ public override int Execute()
}
else
{
+ var manifestInfoDict = _workloadListHelper.WorkloadResolver.GetInstalledManifests().ToDictionary(info => info.Id, StringComparer.OrdinalIgnoreCase);
+
InstalledWorkloadsCollection installedWorkloads = _workloadListHelper.AddInstalledVsWorkloads(installedList);
Reporter.WriteLine();
PrintableTable> table = new();
@@ -86,8 +89,8 @@ public override int Execute()
table.AddColumn(InformationStrings.WorkloadManfiestVersionColumn, workload =>
{
var m = _workloadListHelper.WorkloadResolver.GetManifestFromWorkload(new WorkloadId(workload.Key));
- return m.Version + "/" +
- new WorkloadManifestInfo(m.Id, m.Version, Path.GetDirectoryName(m.ManifestPath)!).ManifestFeatureBand;
+ var manifestInfo = manifestInfoDict[m.Id];
+ return m.Version + "/" + manifestInfo.ManifestFeatureBand;
});
table.AddColumn(InformationStrings.WorkloadSourceColumn, workload => workload.Value);
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/CachingWorkloadResolver.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/CachingWorkloadResolver.cs
index b25b9eb76a5b..60b35d6c8216 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/CachingWorkloadResolver.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/CachingWorkloadResolver.cs
@@ -139,7 +139,7 @@ private static ResolutionResult Resolve(string sdkReferenceName, IWorkloadManife
else if (sdkReferenceName.Equals("Microsoft.NET.SDK.WorkloadManifestTargetsLocator", StringComparison.OrdinalIgnoreCase))
{
List workloadManifestPaths = new List();
- foreach (var manifestDirectory in manifestProvider.GetManifestDirectories())
+ foreach (var manifestDirectory in manifestProvider.GetManifests().Select(m => m.ManifestDirectory))
{
var workloadManifestTargetPath = Path.Combine(manifestDirectory, "WorkloadManifest.targets");
if (File.Exists(workloadManifestTargetPath))
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadManifestProvider.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadManifestProvider.cs
index 473357998678..7aa93f9f998c 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadManifestProvider.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadManifestProvider.cs
@@ -15,8 +15,6 @@ public interface IWorkloadManifestProvider
{
IEnumerable GetManifests();
- IEnumerable GetManifestDirectories();
-
string GetSdkFeatureBand();
}
}
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/ReadableWorkloadManifest.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/ReadableWorkloadManifest.cs
index 512f284019b7..1ce6d96efb15 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/ReadableWorkloadManifest.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/ReadableWorkloadManifest.cs
@@ -14,17 +14,24 @@ namespace Microsoft.NET.Sdk.WorkloadManifestReader
public class ReadableWorkloadManifest
{
public string ManifestId { get; }
+
+ public string ManifestDirectory { get; }
+
public string ManifestPath { get; }
+ public string ManifestFeatureBand { get; }
+
readonly Func _openManifestStreamFunc;
readonly Func _openLocalizationStream;
- public ReadableWorkloadManifest(string manifestId, string manifestPath, Func openManifestStreamFunc, Func openLocalizationStream)
+ public ReadableWorkloadManifest(string manifestId, string manifestDirectory, string manifestPath, string manifestFeatureBand, Func openManifestStreamFunc, Func openLocalizationStream)
{
ManifestId = manifestId;
ManifestPath = manifestPath;
+ ManifestDirectory = manifestDirectory;
+ ManifestFeatureBand = manifestFeatureBand;
_openManifestStreamFunc = openManifestStreamFunc;
_openLocalizationStream = openLocalizationStream;
}
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.GlobalJsonReader.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.GlobalJsonReader.cs
index 20027af22f33..1b28c9a043b6 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.GlobalJsonReader.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.GlobalJsonReader.cs
@@ -23,7 +23,7 @@ public partial class SdkDirectoryWorkloadManifestProvider
{
static class GlobalJsonReader
{
- public static string? GetWorkloadVersionFromGlobalJson(string globalJsonPath)
+ public static string? GetWorkloadVersionFromGlobalJson(string? globalJsonPath)
{
if (string.IsNullOrEmpty(globalJsonPath))
{
@@ -48,7 +48,7 @@ static class GlobalJsonReader
string? workloadVersion = null;
- ConsumeToken(ref reader, JsonTokenType.StartObject);
+ JsonReader.ConsumeToken(ref reader, JsonTokenType.StartObject);
while (reader.Read())
{
switch (reader.TokenType)
@@ -57,7 +57,7 @@ static class GlobalJsonReader
var propName = reader.GetString();
if (string.Equals("sdk", propName, StringComparison.OrdinalIgnoreCase))
{
- ConsumeToken(ref reader, JsonTokenType.StartObject);
+ JsonReader.ConsumeToken(ref reader, JsonTokenType.StartObject);
bool readingSdk = true;
while (readingSdk && reader.Read())
@@ -68,112 +68,36 @@ static class GlobalJsonReader
var sdkPropName = reader.GetString();
if (string.Equals("workloadVersion", sdkPropName, StringComparison.OrdinalIgnoreCase))
{
- workloadVersion = ReadString(ref reader);
+ workloadVersion = JsonReader.ReadString(ref reader);
}
else
{
- ConsumeValue(ref reader);
+ JsonReader.ConsumeValue(ref reader);
}
break;
case JsonTokenType.EndObject:
readingSdk = false;
break;
default:
- throw new GlobalJsonFormatException(Strings.UnexpectedTokenAtOffset, reader.TokenType, reader.TokenStartIndex);
+ throw new JsonFormatException(Strings.UnexpectedTokenAtOffset, reader.TokenType, reader.TokenStartIndex);
}
}
}
else
{
- ConsumeValue(ref reader);
+ JsonReader.ConsumeValue(ref reader);
}
break;
case JsonTokenType.EndObject:
return workloadVersion;
default:
- throw new GlobalJsonFormatException(Strings.UnexpectedTokenAtOffset, reader.TokenType, reader.TokenStartIndex);
+ throw new JsonFormatException(Strings.UnexpectedTokenAtOffset, reader.TokenType, reader.TokenStartIndex);
}
}
- throw new GlobalJsonFormatException(Strings.IncompleteDocument);
+ throw new JsonFormatException(Strings.IncompleteDocument);
}
-
- ///
- /// this expects the reader to be before the value token, and leaves it on the last token of the value
- ///
- private static bool ConsumeValue(ref Utf8JsonStreamReader reader)
- {
- if (!reader.Read())
- {
- return false;
- }
-
- var tokenType = reader.TokenType;
- if (tokenType != JsonTokenType.StartArray && tokenType != JsonTokenType.StartObject)
- {
- return true;
- }
-
- var depth = reader.CurrentDepth;
- do
- {
- if (!reader.Read())
- {
- return false;
- }
- } while (reader.CurrentDepth > depth);
-
- return true;
- }
-
- private static void ConsumeToken(ref Utf8JsonStreamReader reader, JsonTokenType expected)
- {
- if (reader.Read() && expected == reader.TokenType)
- {
- return;
- }
- ThrowUnexpectedTokenException(ref reader, expected);
- }
-
- private static void ThrowUnexpectedTokenException(ref Utf8JsonStreamReader reader, JsonTokenType expected)
- {
- string key;
- if (expected.IsBool())
- {
- key = Strings.ExpectedBoolAtOffset;
- }
- else if (expected.IsInt())
- {
- key = Strings.ExpectedIntegerAtOffset;
- }
- else if (expected == JsonTokenType.String)
- {
- key = Strings.ExpectedStringAtOffset;
- }
- else
- {
- throw new GlobalJsonFormatException(Strings.ExpectedTokenAtOffset, expected, reader.TokenStartIndex);
- }
-
- throw new GlobalJsonFormatException(key, reader.TokenStartIndex);
- }
-
- private static string ReadString(ref Utf8JsonStreamReader reader)
- {
- ConsumeToken(ref reader, JsonTokenType.String);
- return reader.GetString();
- }
- }
-
- [Serializable]
- internal class GlobalJsonFormatException : Exception
- {
- public GlobalJsonFormatException() { }
- public GlobalJsonFormatException(string messageFormat, params object?[] args) : base(string.Format(messageFormat, args)) { }
- public GlobalJsonFormatException(string message) : base(message) { }
- public GlobalJsonFormatException(string message, Exception inner) : base(message, inner) { }
- protected GlobalJsonFormatException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
}
}
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.InstallStateReader.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.InstallStateReader.cs
new file mode 100644
index 000000000000..903f420b7a2b
--- /dev/null
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.InstallStateReader.cs
@@ -0,0 +1,109 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Microsoft.NET.Sdk.Localization;
+using static Microsoft.NET.Sdk.WorkloadManifestReader.WorkloadManifestReader;
+using System.Runtime.Serialization;
+using Microsoft.Deployment.DotNet.Releases;
+
+#if USE_SYSTEM_TEXT_JSON
+using System.Text.Json;
+#else
+using Newtonsoft.Json;
+using JsonTokenType = Newtonsoft.Json.JsonToken;
+#endif
+
+namespace Microsoft.NET.Sdk.WorkloadManifestReader
+{
+ public partial class SdkDirectoryWorkloadManifestProvider
+ {
+ class InstallState
+ {
+ public string? WorkloadSetVersion { get; set; }
+ public WorkloadSet? Manifests { get; set; }
+ }
+
+ static class InstallStateReader
+ {
+ public static InstallState ReadInstallState(string installStatePath)
+ {
+ using var fileStream = File.OpenRead(installStatePath);
+
+#if USE_SYSTEM_TEXT_JSON
+ var readerOptions = new JsonReaderOptions
+ {
+ AllowTrailingCommas = true,
+ CommentHandling = JsonCommentHandling.Skip
+ };
+ var reader = new Utf8JsonStreamReader(fileStream, readerOptions);
+#else
+ using var textReader = new StreamReader(fileStream, System.Text.Encoding.UTF8, true);
+ using var jsonReader = new JsonTextReader(textReader);
+
+ var reader = new Utf8JsonStreamReader(jsonReader);
+#endif
+
+ InstallState installState = new();
+
+ JsonReader.ConsumeToken(ref reader, JsonTokenType.StartObject);
+ while (reader.Read())
+ {
+ switch (reader.TokenType)
+ {
+ case JsonTokenType.PropertyName:
+ var propName = reader.GetString();
+ if (string.Equals("workloadVersion", propName, StringComparison.OrdinalIgnoreCase))
+ {
+ installState.WorkloadSetVersion = JsonReader.ReadString(ref reader);
+ }
+ else if (string.Equals("manifests", propName, StringComparison.OrdinalIgnoreCase))
+ {
+ installState.Manifests = ReadManifests(ref reader);
+ }
+ else
+ {
+ JsonReader.ConsumeValue(ref reader);
+ }
+ break;
+
+ case JsonTokenType.EndObject:
+ return installState;
+ default:
+ throw new JsonFormatException(Strings.UnexpectedTokenAtOffset, reader.TokenType, reader.TokenStartIndex);
+ }
+ }
+
+ throw new JsonFormatException(Strings.IncompleteDocument);
+ }
+
+ static WorkloadSet ReadManifests(ref Utf8JsonStreamReader reader)
+ {
+ JsonReader.ConsumeToken(ref reader, JsonTokenType.StartObject);
+ Dictionary workloadSetDict = new();
+
+ while (reader.Read())
+ {
+ switch (reader.TokenType)
+ {
+ case JsonTokenType.PropertyName:
+ var propName = reader.GetString();
+ var propValue = JsonReader.ReadString(ref reader);
+ workloadSetDict[propName] = propValue;
+ break;
+ case JsonTokenType.EndObject:
+ return WorkloadSet.FromDictionaryForJson(workloadSetDict, new SdkFeatureBand(new ReleaseVersion(0,0,0)));
+ default:
+ throw new JsonFormatException(Strings.UnexpectedTokenAtOffset, reader.TokenType, reader.TokenStartIndex);
+ }
+ }
+ throw new JsonFormatException(Strings.IncompleteDocument);
+ }
+ }
+ }
+}
+
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.JsonReader.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.JsonReader.cs
new file mode 100644
index 000000000000..6045ad1e653c
--- /dev/null
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.JsonReader.cs
@@ -0,0 +1,105 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Microsoft.NET.Sdk.Localization;
+using static Microsoft.NET.Sdk.WorkloadManifestReader.WorkloadManifestReader;
+using System.Runtime.Serialization;
+
+#if USE_SYSTEM_TEXT_JSON
+using System.Text.Json;
+#else
+using Newtonsoft.Json;
+using JsonTokenType = Newtonsoft.Json.JsonToken;
+#endif
+
+namespace Microsoft.NET.Sdk.WorkloadManifestReader
+{
+ public partial class SdkDirectoryWorkloadManifestProvider
+ {
+ static class JsonReader
+ {
+
+ ///
+ /// this expects the reader to be before the value token, and leaves it on the last token of the value
+ ///
+ internal static bool ConsumeValue(ref Utf8JsonStreamReader reader)
+ {
+ if (!reader.Read())
+ {
+ return false;
+ }
+
+ var tokenType = reader.TokenType;
+ if (tokenType != JsonTokenType.StartArray && tokenType != JsonTokenType.StartObject)
+ {
+ return true;
+ }
+
+ var depth = reader.CurrentDepth;
+ do
+ {
+ if (!reader.Read())
+ {
+ return false;
+ }
+ } while (reader.CurrentDepth > depth);
+
+ return true;
+ }
+
+ internal static void ConsumeToken(ref Utf8JsonStreamReader reader, JsonTokenType expected)
+ {
+ if (reader.Read() && expected == reader.TokenType)
+ {
+ return;
+ }
+ ThrowUnexpectedTokenException(ref reader, expected);
+ }
+
+ private static void ThrowUnexpectedTokenException(ref Utf8JsonStreamReader reader, JsonTokenType expected)
+ {
+ string key;
+ if (expected.IsBool())
+ {
+ key = Strings.ExpectedBoolAtOffset;
+ }
+ else if (expected.IsInt())
+ {
+ key = Strings.ExpectedIntegerAtOffset;
+ }
+ else if (expected == JsonTokenType.String)
+ {
+ key = Strings.ExpectedStringAtOffset;
+ }
+ else
+ {
+ throw new JsonFormatException(Strings.ExpectedTokenAtOffset, expected, reader.TokenStartIndex);
+ }
+
+ throw new JsonFormatException(key, reader.TokenStartIndex);
+ }
+
+ internal static string ReadString(ref Utf8JsonStreamReader reader)
+ {
+ ConsumeToken(ref reader, JsonTokenType.String);
+ return reader.GetString();
+ }
+ }
+
+ [Serializable]
+ internal class JsonFormatException : Exception
+ {
+ public JsonFormatException() { }
+ public JsonFormatException(string messageFormat, params object?[] args) : base(string.Format(messageFormat, args)) { }
+ public JsonFormatException(string message) : base(message) { }
+ public JsonFormatException(string message, Exception inner) : base(message, inner) { }
+ protected JsonFormatException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+ }
+}
+
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs
index 20f1ef347ed9..f153166568e1 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs
@@ -25,6 +25,8 @@ public partial class SdkDirectoryWorkloadManifestProvider : IWorkloadManifestPro
private readonly Dictionary? _knownManifestIdsAndOrder;
private readonly WorkloadSet? _workloadSet;
+ private readonly WorkloadSet? _manifestsFromInstallState;
+ private readonly string? _installStateFilePath;
public SdkDirectoryWorkloadManifestProvider(string sdkRootPath, string sdkVersion, string? userProfileDir, string? globalJsonPath)
: this(sdkRootPath, sdkVersion, Environment.GetEnvironmentVariable, userProfileDir, globalJsonPath)
@@ -91,15 +93,29 @@ internal SdkDirectoryWorkloadManifestProvider(string sdkRootPath, string sdkVers
var availableWorkloadSets = GetAvailableWorkloadSets();
- if (globalJsonPath != null)
+ string? globalJsonWorkloadSetVersion = GlobalJsonReader.GetWorkloadVersionFromGlobalJson(globalJsonPath);
+ if (globalJsonWorkloadSetVersion != null)
{
- string? globalJsonWorkloadSetVersion = GlobalJsonReader.GetWorkloadVersionFromGlobalJson(globalJsonPath);
- if (globalJsonWorkloadSetVersion != null)
+ if (!availableWorkloadSets.TryGetValue(globalJsonWorkloadSetVersion, out _workloadSet))
{
- if (!availableWorkloadSets.TryGetValue(globalJsonWorkloadSetVersion, out _workloadSet))
+ throw new FileNotFoundException(string.Format(Strings.WorkloadVersionFromGlobalJsonNotFound, globalJsonWorkloadSetVersion, globalJsonPath));
+ }
+ }
+ else
+ {
+ var installStateFilePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkVersionBand, _sdkRootPath), "default.json");
+ if (File.Exists(installStateFilePath))
+ {
+ var installState = InstallStateReader.ReadInstallState(installStateFilePath);
+ if (!string.IsNullOrEmpty(installState.WorkloadSetVersion))
{
- throw new FileNotFoundException(string.Format(Strings.WorkloadVersionFromGlobalJsonNotFound, globalJsonWorkloadSetVersion, globalJsonPath));
+ if (!availableWorkloadSets.TryGetValue(installState.WorkloadSetVersion!, out _workloadSet))
+ {
+ throw new FileNotFoundException(string.Format(Strings.WorkloadVersionFromInstallStateNotFound, installState.WorkloadSetVersion, installStateFilePath));
+ }
}
+ _manifestsFromInstallState = installState.Manifests;
+ _installStateFilePath = installStateFilePath;
}
}
@@ -112,31 +128,30 @@ internal SdkDirectoryWorkloadManifestProvider(string sdkRootPath, string sdkVers
public IEnumerable GetManifests()
{
- foreach (var workloadManifestDirectory in GetManifestDirectories())
+ // Scan manifest directories
+ var manifestIdsToManifests = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ void AddManifest(string manifestId, string manifestDirectory, string featureBand)
{
- var workloadManifestPath = Path.Combine(workloadManifestDirectory, "WorkloadManifest.json");
- var id = Path.GetFileName(workloadManifestDirectory);
+ var workloadManifestPath = Path.Combine(manifestDirectory, "WorkloadManifest.json");
- yield return new(
- id,
+ var readableManifest = new ReadableWorkloadManifest(
+ manifestId,
+ manifestDirectory,
workloadManifestPath,
+ featureBand,
() => File.OpenRead(workloadManifestPath),
- () => WorkloadManifestReader.TryOpenLocalizationCatalogForManifest(workloadManifestPath)
- );
- }
- }
+ () => WorkloadManifestReader.TryOpenLocalizationCatalogForManifest(workloadManifestPath));
- public IEnumerable GetManifestDirectories()
- {
- // Scan manifest directories
- var manifestIdsToDirectories = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ manifestIdsToManifests[manifestId] = readableManifest;
+ }
- void ProbeDirectory(string manifestDirectory)
+ void ProbeDirectory(string manifestDirectory, string featureBand)
{
(string? id, string? finalManifestDirectory) = ResolveManifestDirectory(manifestDirectory);
if (id != null && finalManifestDirectory != null)
{
- manifestIdsToDirectories.Add(id, finalManifestDirectory);
+ AddManifest(id, finalManifestDirectory, featureBand);
}
}
@@ -148,7 +163,7 @@ void ProbeDirectory(string manifestDirectory)
{
foreach (var workloadManifestDirectory in Directory.EnumerateDirectories(manifestVersionBandDirectory))
{
- ProbeDirectory(workloadManifestDirectory);
+ ProbeDirectory(workloadManifestDirectory, _sdkVersionBand.ToString());
}
}
}
@@ -170,7 +185,7 @@ void ProbeDirectory(string manifestDirectory)
foreach (var workloadManifestDirectory in directoriesWithManifests.Values)
{
- ProbeDirectory(workloadManifestDirectory);
+ ProbeDirectory(workloadManifestDirectory, _sdkVersionBand.ToString());
}
}
@@ -179,26 +194,48 @@ void ProbeDirectory(string manifestDirectory)
{
foreach (var kvp in _workloadSet.ManifestVersions)
{
- manifestIdsToDirectories[kvp.Key.ToString()] = GetManifestDirectoryFromSpecifier(new ManifestSpecifier(kvp.Key, kvp.Value.Version, kvp.Value.FeatureBand));
+ var manifestSpecifier = new ManifestSpecifier(kvp.Key, kvp.Value.Version, kvp.Value.FeatureBand);
+ var manifestDirectory = GetManifestDirectoryFromSpecifier(manifestSpecifier);
+ if (manifestDirectory == null)
+ {
+ throw new FileNotFoundException(string.Format(Strings.ManifestFromWorkloadSetNotFound, manifestSpecifier.ToString(), _workloadSet.Version));
+ }
+ AddManifest(manifestSpecifier.Id.ToString(), manifestDirectory, manifestSpecifier.FeatureBand.ToString());
+
+ }
+ }
+
+ // Load manifests from install state
+ if (_manifestsFromInstallState != null)
+ {
+ foreach (var kvp in _manifestsFromInstallState.ManifestVersions)
+ {
+ var manifestSpecifier = new ManifestSpecifier(kvp.Key, kvp.Value.Version, kvp.Value.FeatureBand);
+ var manifestDirectory = GetManifestDirectoryFromSpecifier(manifestSpecifier);
+ if (manifestDirectory == null)
+ {
+ throw new FileNotFoundException(string.Format(Strings.ManifestFromInstallStateNotFound, manifestSpecifier.ToString(), _installStateFilePath));
+ }
+ AddManifest(manifestSpecifier.Id.ToString(), manifestDirectory, manifestSpecifier.FeatureBand.ToString());
}
}
- if (_knownManifestIdsAndOrder != null && _knownManifestIdsAndOrder.Keys.Any(id => !manifestIdsToDirectories.ContainsKey(id)))
+ if (_knownManifestIdsAndOrder != null && _knownManifestIdsAndOrder.Keys.Any(id => !manifestIdsToManifests.ContainsKey(id)))
{
- var missingManifestIds = _knownManifestIdsAndOrder.Keys.Where(id => !manifestIdsToDirectories.ContainsKey(id));
+ var missingManifestIds = _knownManifestIdsAndOrder.Keys.Where(id => !manifestIdsToManifests.ContainsKey(id));
foreach (var missingManifestId in missingManifestIds)
{
- var manifestDir = FallbackForMissingManifest(missingManifestId);
+ var (manifestDir, featureBand) = FallbackForMissingManifest(missingManifestId);
if (!string.IsNullOrEmpty(manifestDir))
{
- manifestIdsToDirectories.Add(missingManifestId, manifestDir);
+ AddManifest(missingManifestId, manifestDir, featureBand);
}
}
}
// Return manifests in a stable order. Manifests in the KnownWorkloadManifests.txt file will be first, and in the same order they appear in that file.
// Then the rest of the manifests (if any) will be returned in (ordinal case-insensitive) alphabetical order.
- return manifestIdsToDirectories
+ return manifestIdsToManifests
.OrderBy(kvp =>
{
if (_knownManifestIdsAndOrder != null &&
@@ -250,13 +287,13 @@ void ProbeDirectory(string manifestDirectory)
return (null, null);
}
- private string FallbackForMissingManifest(string manifestId)
+ private (string manifestDirectory, string manifestFeatureBand) FallbackForMissingManifest(string manifestId)
{
// Only use the last manifest root (usually the dotnet folder itself) for fallback
var sdkManifestPath = _manifestRoots.Last();
if (!Directory.Exists(sdkManifestPath))
{
- return string.Empty;
+ return (string.Empty, string.Empty);
}
var candidateFeatureBands = Directory.GetDirectories(sdkManifestPath)
@@ -264,7 +301,7 @@ private string FallbackForMissingManifest(string manifestId)
.Select(featureBand => new SdkFeatureBand(featureBand))
.Where(featureBand => featureBand < _sdkVersionBand || _sdkVersionBand.ToStringWithoutPrerelease().Equals(featureBand.ToString(), StringComparison.Ordinal));
- var matchingManifestFatureBandsAndResolvedManifestDirectories = candidateFeatureBands
+ var matchingManifestFeatureBandsAndResolvedManifestDirectories = candidateFeatureBands
// Calculate path to \
.Select(featureBand => (featureBand, manifestDirectory: Path.Combine(sdkManifestPath, featureBand.ToString(), manifestId)))
// Filter out directories that don't exist
@@ -275,18 +312,19 @@ private string FallbackForMissingManifest(string manifestId)
.Where(t => t.res.id != null && t.res.manifestDirectory != null)
.ToList();
- if (matchingManifestFatureBandsAndResolvedManifestDirectories.Any())
+ if (matchingManifestFeatureBandsAndResolvedManifestDirectories.Any())
{
- return matchingManifestFatureBandsAndResolvedManifestDirectories.OrderByDescending(t => t.featureBand).First().res.manifestDirectory!;
+ var selectedFeatureBandAndManifestDirectory = matchingManifestFeatureBandsAndResolvedManifestDirectories.OrderByDescending(t => t.featureBand).First();
+ return (selectedFeatureBandAndManifestDirectory.res.manifestDirectory!, selectedFeatureBandAndManifestDirectory.featureBand.ToString());
}
else
{
// Manifest does not exist
- return string.Empty;
+ return (string.Empty, string.Empty);
}
}
- private string GetManifestDirectoryFromSpecifier(ManifestSpecifier manifestSpecifier)
+ private string? GetManifestDirectoryFromSpecifier(ManifestSpecifier manifestSpecifier)
{
foreach (var manifestDirectory in _manifestRoots)
{
@@ -297,8 +335,7 @@ private string GetManifestDirectoryFromSpecifier(ManifestSpecifier manifestSpeci
return specifiedManifestDirectory;
}
}
-
- throw new FileNotFoundException(string.Format(Strings.SpecifiedManifestNotFound, manifestSpecifier.ToString()));
+ return null;
}
///
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/Strings.resx b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/Strings.resx
index c728ad1b1252..51881eb4abec 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/Strings.resx
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/Strings.resx
@@ -189,11 +189,20 @@
Invalid version: {0}
-
- Specified workload manifest was not found: {0}
+
+ Workload manifest {0} from workload version {1} was not installed. Running "dotnet workload repair" may resolve this.
+ {Locked="dotnet workload repair"}
+
+
+ Workload manifest {0}, which was specified in {1}, was not found. Running "dotnet workload repair" may resolve this.
+ {Locked="dotnet workload repair"}
Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.
+ {Locked="dotnet workload restore"}
+
+
+ Workload version {0}, which was specified in {1}, was not found.
Error parsing version '{1}' for workload manifest ID '{0}'
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/TempDirectoryWorkloadManifestProvider.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/TempDirectoryWorkloadManifestProvider.cs
index 6ae5e948fb6c..757807105bc6 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/TempDirectoryWorkloadManifestProvider.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/TempDirectoryWorkloadManifestProvider.cs
@@ -35,7 +35,9 @@ public IEnumerable
yield return new(
manifestId,
+ workloadManifestDirectory,
workloadManifestPath,
+ _sdkVersionBand,
() => File.OpenRead(workloadManifestPath),
() => WorkloadManifestReader.TryOpenLocalizationCatalogForManifest(workloadManifestPath)
);
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadInstallType.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadInstallType.cs
new file mode 100644
index 000000000000..95d62da17dc9
--- /dev/null
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadInstallType.cs
@@ -0,0 +1,68 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.NET.Sdk.WorkloadManifestReader
+{
+ ///
+ /// Describes different workload installation types.
+ ///
+ public enum InstallType
+ {
+ ///
+ /// Workloads are installed as NuGet packages
+ ///
+ FileBased = 0,
+ ///
+ /// Workloads are installed as MSIs.
+ ///
+ Msi = 1
+ }
+
+ public static class WorkloadInstallType
+ {
+ ///
+ /// Determines the associated with a specific SDK version.
+ ///
+ /// The SDK version to check.
+ /// The associated with the SDK.
+ public static InstallType GetWorkloadInstallType(SdkFeatureBand sdkFeatureBand, string dotnetDir)
+ {
+ string installerTypePath = Path.Combine(dotnetDir, "metadata",
+ "workloads", $"{sdkFeatureBand.ToStringWithoutPrerelease()}", "installertype");
+
+ if (File.Exists(Path.Combine(installerTypePath, "msi")))
+ {
+ return InstallType.Msi;
+ }
+
+ return InstallType.FileBased;
+ }
+
+ public static string GetInstallStateFolder(SdkFeatureBand sdkFeatureBand, string dotnetDir)
+ {
+ var installType = GetWorkloadInstallType(sdkFeatureBand, dotnetDir);
+
+ if (installType == InstallType.FileBased)
+ {
+ return Path.Combine(dotnetDir, "metadata", "workloads", sdkFeatureBand.ToString(), "InstallState");
+ }
+ else if (installType == InstallType.Msi)
+ {
+ return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "dotnet", "workloads", sdkFeatureBand.ToString(), "InstallState");
+ }
+ else
+ {
+ throw new ArgumentException("Unexpected InstallType: " + installType);
+ }
+ }
+ }
+}
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadManifestInfo.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadManifestInfo.cs
index 4ebc88bb0fcf..12dfe6fc7737 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadManifestInfo.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadManifestInfo.cs
@@ -13,12 +13,12 @@ namespace Microsoft.NET.Sdk.WorkloadManifestReader
{
public class WorkloadManifestInfo
{
- public WorkloadManifestInfo(string id, string version, string manifestDirectory)
+ public WorkloadManifestInfo(string id, string version, string manifestDirectory, string manifestFeatureBand)
{
Id = id;
Version = version;
ManifestDirectory = manifestDirectory;
- ManifestFeatureBand = Path.GetFileName(Path.GetDirectoryName(manifestDirectory))!;
+ ManifestFeatureBand = manifestFeatureBand;
}
public string Id { get; }
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadResolver.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadResolver.cs
index d13eb54c6e51..d9b0d4a73334 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadResolver.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadResolver.cs
@@ -21,7 +21,7 @@ namespace Microsoft.NET.Sdk.WorkloadManifestReader
///
public class WorkloadResolver : IWorkloadResolver
{
- private readonly Dictionary _manifests = new(StringComparer.OrdinalIgnoreCase);
+ private readonly Dictionary _manifests = new(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary _workloads = new();
private readonly Dictionary _packs = new();
private IWorkloadManifestProvider? _manifestProvider;
@@ -118,9 +118,10 @@ private void LoadManifestsFromProvider(IWorkloadManifestProvider manifestProvide
using (Stream? localizationStream = readableManifest.OpenLocalizationStream())
{
var manifest = WorkloadManifestReader.ReadWorkloadManifest(readableManifest.ManifestId, manifestStream, localizationStream, readableManifest.ManifestPath);
- if (!_manifests.TryAdd(readableManifest.ManifestId, manifest))
+ var manifestInfo = new WorkloadManifestInfo(manifest.Id, manifest.Version, readableManifest.ManifestDirectory, readableManifest.ManifestFeatureBand);
+ if (!_manifests.TryAdd(readableManifest.ManifestId, (manifest, manifestInfo)))
{
- var existingManifest = _manifests[readableManifest.ManifestId];
+ var existingManifest = _manifests[readableManifest.ManifestId].manifest;
throw new WorkloadManifestCompositionException(Strings.DuplicateManifestID, manifestProvider.GetType().FullName, readableManifest.ManifestId, readableManifest.ManifestPath, existingManifest.ManifestPath);
}
}
@@ -134,14 +135,15 @@ private void ComposeWorkloadManifests()
Dictionary? redirects = null;
- foreach (var manifest in _manifests.Values)
+ foreach (var (manifest, info) in _manifests.Values)
{
if (manifest.DependsOnManifests != null)
{
foreach (var dependency in manifest.DependsOnManifests)
{
- if (_manifests.TryGetValue(dependency.Key, out var resolvedDependency))
+ if (_manifests.TryGetValue(dependency.Key, out var t))
{
+ var resolvedDependency = t.manifest;
if (FXVersion.Compare(dependency.Value, resolvedDependency.ParsedVersion) > 0)
{
throw new WorkloadManifestCompositionException(Strings.ManifestDependencyVersionTooLow, dependency.Key, resolvedDependency.Version, dependency.Value, manifest.Id, manifest.ManifestPath);
@@ -711,11 +713,17 @@ private bool IsWorkloadPlatformCompatible(WorkloadDefinition workload, WorkloadM
private bool IsWorkloadImplicitlyAbstract(WorkloadDefinition workload, WorkloadManifest manifest) => !GetPacksInWorkload(workload, manifest).Any();
- public string GetManifestVersion(string manifestId) =>
- (_manifests.TryGetValue(manifestId, out WorkloadManifest? value)? value : null)?.Version
- ?? throw new Exception($"Manifest with id {manifestId} does not exist.");
+ public string GetManifestVersion(string manifestId)
+ {
+ if (_manifests.TryGetValue(manifestId, out var value))
+ {
+ return value.manifest.Version;
+ }
+ throw new Exception($"Manifest with id {manifestId} does not exist.");
+ }
+
- public IEnumerable GetInstalledManifests() => _manifests.Select(m => new WorkloadManifestInfo(m.Value.Id, m.Value.Version, Path.GetDirectoryName(m.Value.ManifestPath)!));
+ public IEnumerable GetInstalledManifests() => _manifests.Select(t => t.Value.info);
}
static class DictionaryExtensions
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadSet.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadSet.cs
index ed78e7765c98..2d2606cc1b3f 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadSet.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadSet.cs
@@ -71,7 +71,12 @@ public static WorkloadSet FromDictionaryForJson(IDictionary dict
public static WorkloadSet FromJson(string json, SdkFeatureBand defaultFeatureBand)
{
#if USE_SYSTEM_TEXT_JSON
- return FromDictionaryForJson(JsonSerializer.Deserialize>(json)!, defaultFeatureBand);
+ var jsonSerializerOptions = new JsonSerializerOptions()
+ {
+ AllowTrailingCommas = true,
+ ReadCommentHandling = JsonCommentHandling.Skip
+ };
+ return FromDictionaryForJson(JsonSerializer.Deserialize>(json, jsonSerializerOptions)!, defaultFeatureBand);
#else
return FromDictionaryForJson(JsonConvert.DeserializeObject>(json)!, defaultFeatureBand);
#endif
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.cs.xlf b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.cs.xlf
index bf04928a9a37..87af4878ea91 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.cs.xlf
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.cs.xlf
@@ -77,6 +77,16 @@
Závislost manifestu úlohy {0} verze {1} je nižší než verze {2} požadovaná manifestem {3} [{4}].
+
+
+ Workload manifest {0}, which was specified in {1}, was not found. Running "dotnet workload repair" may resolve this.
+ {Locked="dotnet workload repair"}
+
+
+
+ Workload manifest {0} from workload version {1} was not installed. Running "dotnet workload repair" may resolve this.
+ {Locked="dotnet workload repair"}
+
Nepovedlo se najít úlohu {0} rozšířenou podle úlohy {1} v manifestu {2} [{3}].
@@ -102,11 +112,6 @@
Přesměrování úlohy {0} má jiné klíče než redirect-to.
-
-
- Zadaný manifest úlohy se nenašel: {0}
-
-
Neočekávaný token {0} u posunu {1}
@@ -134,7 +139,12 @@
- Verze {0} úlohy, která byla zadána v {1}, nebyla nalezena. Spuštěním příkazu dotnet workload restore nainstalujte tuto verzi úlohy.
+ Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.
+ {Locked="dotnet workload restore"}
+
+
+
+ Workload version {0}, which was specified in {1}, was not found.