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 072a079630a9..000000000000
--- a/src/Cli/dotnet/commands/dotnet-workload/InstallType.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-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 9afbb3106206..70772bbed7af 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandParser.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandParser.cs
@@ -78,8 +78,9 @@ internal static void ShowWorkloadsInfo(ParseResult parseResult = null, IWorkload
reporter.WriteLine($" {workloadManifest.ManifestPath,align}");
reporter.Write($"{separator}{CommonStrings.WorkloadInstallTypeColumn}:");
- reporter.WriteLine($" {WorkloadInstallerFactory.GetWorkloadInstallType(new SdkFeatureBand(workloadFeatureBand), dotnetPath),align}"
+ reporter.WriteLine($" {WorkloadInstallType.GetWorkloadInstallType(new SdkFeatureBand(workloadFeatureBand), dotnetPath).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 c2e3f60e498e..67b0734916f9 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)
{
@@ -62,24 +62,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.WorkloadMSBuildSdkResolver/CachingWorkloadResolver.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/CachingWorkloadResolver.cs
index 126b751316b0..15b57a1a9da0 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 9b1073ccce14..85b502f2d1df 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 c09bb13ac569..5974089df81e 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/ReadableWorkloadManifest.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/ReadableWorkloadManifest.cs
@@ -13,6 +13,9 @@ namespace Microsoft.NET.Sdk.WorkloadManifestReader
public class ReadableWorkloadManifest
{
public string ManifestId { get; }
+
+ public string ManifestDirectory { get; }
+
public string ManifestPath { get; }
readonly Func _openManifestStreamFunc;
@@ -20,10 +23,11 @@ public class ReadableWorkloadManifest
readonly Func _openLocalizationStream;
- public ReadableWorkloadManifest(string manifestId, string manifestPath, Func openManifestStreamFunc, Func openLocalizationStream)
+ public ReadableWorkloadManifest(string manifestId, string manifestDirectory, string manifestPath, Func openManifestStreamFunc, Func openLocalizationStream)
{
ManifestId = manifestId;
ManifestPath = manifestPath;
+ ManifestDirectory = manifestDirectory;
_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 2b04bc7e84d5..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,115 +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) { }
- #if NET8_0_OR_GREATER
- [Obsolete(DiagnosticId = "SYSLIB0051")] // add this attribute to the serialization ctor
- #endif
- 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..79af53599ed1
--- /dev/null
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.JsonReader.cs
@@ -0,0 +1,108 @@
+// 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) { }
+#if NET8_0_OR_GREATER
+ [Obsolete(DiagnosticId = "SYSLIB0051")] // add this attribute to the serialization ctor
+#endif
+ 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 866f886de85d..93c07b1e5fa9 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs
@@ -24,6 +24,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)
@@ -90,15 +92,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;
}
}
@@ -110,22 +126,6 @@ internal SdkDirectoryWorkloadManifestProvider(string sdkRootPath, string sdkVers
}
public IEnumerable GetManifests()
- {
- foreach (var workloadManifestDirectory in GetManifestDirectories())
- {
- var workloadManifestPath = Path.Combine(workloadManifestDirectory, "WorkloadManifest.json");
- var id = Path.GetFileName(workloadManifestDirectory);
-
- yield return new(
- id,
- workloadManifestPath,
- () => File.OpenRead(workloadManifestPath),
- () => WorkloadManifestReader.TryOpenLocalizationCatalogForManifest(workloadManifestPath)
- );
- }
- }
-
- public IEnumerable GetManifestDirectories()
{
// Scan manifest directories
var manifestIdsToDirectories = new Dictionary(StringComparer.OrdinalIgnoreCase);
@@ -178,7 +178,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;
}
}
@@ -208,7 +229,19 @@ void ProbeDirectory(string manifestDirectory)
return int.MaxValue;
})
.ThenBy(kvp => kvp.Key, StringComparer.OrdinalIgnoreCase)
- .Select(kvp => kvp.Value)
+ .Select(kvp =>
+ {
+ var manifestId = kvp.Key;
+ var manifestDirectory = kvp.Value;
+ var workloadManifestPath = Path.Combine(manifestDirectory, "WorkloadManifest.json");
+
+ return new ReadableWorkloadManifest(
+ manifestId,
+ manifestDirectory,
+ workloadManifestPath,
+ () => File.OpenRead(workloadManifestPath),
+ () => WorkloadManifestReader.TryOpenLocalizationCatalogForManifest(workloadManifestPath));
+ })
.ToList();
}
@@ -285,7 +318,7 @@ private string FallbackForMissingManifest(string manifestId)
}
}
- private string GetManifestDirectoryFromSpecifier(ManifestSpecifier manifestSpecifier)
+ private string? GetManifestDirectoryFromSpecifier(ManifestSpecifier manifestSpecifier)
{
foreach (var manifestDirectory in _manifestRoots)
{
@@ -296,8 +329,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 ccd1fb7af73c..f5aef412d6ca 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/TempDirectoryWorkloadManifestProvider.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/TempDirectoryWorkloadManifestProvider.cs
@@ -35,6 +35,7 @@ public IEnumerable
yield return new(
manifestId,
+ workloadManifestDirectory,
workloadManifestPath,
() => 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..b484e876c0ad
--- /dev/null
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadInstallType.cs
@@ -0,0 +1,66 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+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..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.