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..ac7cfffaec46 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandParser.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandParser.cs
@@ -65,7 +65,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/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.GlobalJsonReader.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.GlobalJsonReader.cs
index 20027af22f33..7cf79facd6f3 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))
{
@@ -31,24 +31,11 @@ static class GlobalJsonReader
}
using var fileStream = File.OpenRead(globalJsonPath);
-
-#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
+ var reader = JsonReader.CreateReader(fileStream);
string? workloadVersion = null;
- ConsumeToken(ref reader, JsonTokenType.StartObject);
+ JsonReader.ConsumeToken(ref reader, JsonTokenType.StartObject);
while (reader.Read())
{
switch (reader.TokenType)
@@ -57,7 +44,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,113 +55,37 @@ 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);
- }
-
- ///
- /// 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();
+ throw new JsonFormatException(Strings.IncompleteDocument);
}
}
-
- [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..32e47f7df400
--- /dev/null
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.InstallStateReader.cs
@@ -0,0 +1,96 @@
+// 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);
+ var reader = JsonReader.CreateReader(fileStream);
+
+ 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..cd720f834da4
--- /dev/null
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.JsonReader.cs
@@ -0,0 +1,123 @@
+// 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
+ {
+
+ internal static Utf8JsonStreamReader CreateReader(FileStream fileStream)
+ {
+#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
+ return reader;
+ }
+
+ ///
+ /// 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..8c188a3a186f 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)
@@ -102,6 +104,23 @@ internal SdkDirectoryWorkloadManifestProvider(string sdkRootPath, string sdkVers
}
}
}
+ 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))
+ {
+ if (!availableWorkloadSets.TryGetValue(installState.WorkloadSetVersion!, out _workloadSet))
+ {
+ throw new FileNotFoundException(string.Format(Strings.WorkloadVersionFromInstallStateNotFound, installState.WorkloadSetVersion, installStateFilePath));
+ }
+ }
+ _manifestsFromInstallState = installState.Manifests;
+ _installStateFilePath = installStateFilePath;
+ }
+ }
if (_workloadSet == null && availableWorkloadSets.Any())
{
@@ -179,7 +198,28 @@ 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));
+ }
+ manifestIdsToDirectories[kvp.Key.ToString()] = manifestDirectory;
+ }
+ }
+
+ // 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));
+ }
+ manifestIdsToDirectories[kvp.Key.ToString()] = manifestDirectory;
}
}
@@ -286,7 +326,7 @@ private string FallbackForMissingManifest(string manifestId)
}
}
- private string GetManifestDirectoryFromSpecifier(ManifestSpecifier manifestSpecifier)
+ private string? GetManifestDirectoryFromSpecifier(ManifestSpecifier manifestSpecifier)
{
foreach (var manifestDirectory in _manifestRoots)
{
@@ -297,8 +337,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..0d0bb77d4b8e 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/Strings.resx
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/Strings.resx
@@ -189,12 +189,18 @@
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.
+
+
+ Workload manifest {0}, which was specified in {1}, was not found. Running "dotnet workload repair" may resolve this.
Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.
+
+ 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/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/xlf/Strings.cs.xlf b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.cs.xlf
index bf04928a9a37..d239988ced4e 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.
+ Workload manifest {0}, which was specified in {1}, was not found. Running "dotnet workload repair" may resolve this.
+
+
+
+ Workload manifest {0} from workload version {1} was not installed. Running "dotnet workload repair" may resolve this.
+ Workload manifest {0} from workload version {1} was not installed. Running "dotnet workload repair" may resolve this.
+
+
Could not find workload '{0}' extended by workload '{1}' in manifest '{2}' [{3}]
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.
-
- Specified workload manifest was not found: {0}
- Zadaný manifest úlohy se nenašel: {0}
-
-
Unexpected token '{0}' at offset {1}
Neočekávaný token {0} u posunu {1}
@@ -137,6 +142,11 @@
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.
+ Workload version {0}, which was specified in {1}, was not found.
+
+