Skip to content

Commit b77e4ed

Browse files
[workload] dotnet workload --info shows WorkloadDependencies.json
Given the files: C:\Program Files\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.android\36.1.2\WorkloadDependencies.json C:\Program Files\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.ios\26.1.10494\WorkloadDependencies.json C:\Program Files\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.maui\10.0.0\WorkloadDependencies.json This now prints out the information we have available: > D:\src\dotnet\sdk\artifacts\bin\redist\Debug\dotnet\dotnet.exe workload --info Workload version: 10.0.200-manifests.f8cdff57 Configured to use workload sets when installing new manifests. No workload sets are installed. Run "dotnet workload restore" to install a workload set. [android] Installation Source: SDK 10.0.200, VS 18.0.11222.15, VS 17.14.36811.4 Manifest Version: 36.1.2/10.0.200 Manifest Path: D:\src\dotnet\sdk\artifacts\bin\redist\Debug\dotnet\sdk-manifests\10.0.200\microsoft.net.sdk.android\36.1.2\WorkloadManifest.json Install Type: FileBased Dependencies (jdk): Version: [17.0,22.0) Recommended Version: 17.0.14 Dependencies (androidsdk): - Android SDK Build-Tools 35 build-tools;35.0.0 - Android SDK Command-line Tools cmdline-tools;13.0 - Android Emulator (optional) emulator Recommended Version: 35.1.20 - NDK (Side by side) 27.1.12297006 (optional) ndk;27.1.12297006 - Android SDK Platform 35 platforms;android-35 - Android SDK Platform 36.1 (Preview) (optional) platforms;android-36.1 - Android SDK Platform-Tools platform-tools Recommended Version: 35.0.2 - Google APIs ARM 64 v8a System Image (optional) system-images;android-35;google_apis;x86_64 [maccatalyst] Installation Source: VS 18.0.11222.15, VS 17.14.36811.4 Manifest Version: 26.1.10494/10.0.100 Manifest Path: D:\src\dotnet\sdk\artifacts\bin\redist\Debug\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.maccatalyst\26.1.10494\WorkloadManifest.json Install Type: FileBased Dependencies (xcode): Version: [26.1,) Recommended Version: 26.1 Dependencies (sdk): Version: 26.1 [ios] Installation Source: VS 18.0.11222.15, VS 17.14.36811.4 Manifest Version: 26.1.10494/10.0.100 Manifest Path: D:\src\dotnet\sdk\artifacts\bin\redist\Debug\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.ios\26.1.10494\WorkloadManifest.json Install Type: FileBased Dependencies (xcode): Version: [26.1,) Recommended Version: 26.1 Dependencies (sdk): Version: 26.1 [maui-windows] Installation Source: VS 18.0.11222.15, VS 17.14.36811.4 Manifest Version: 10.0.0/10.0.100 Manifest Path: D:\src\dotnet\sdk\artifacts\bin\redist\Debug\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.maui\10.0.0\WorkloadManifest.json Install Type: FileBased Dependencies (windowsAppSdk): Version: [1.7.250909003,) Recommended Version: 1.7.250909003 Dependencies (windowsSdkBuildTools): Version: [10.0.22621.756,) Recommended Version: 10.0.22621.756 Dependencies (win2d): Version: [1.3.2,) Recommended Version: 1.3.2 Dependencies (webview2): Version: [1.0.3179.45,) Recommended Version: 1.0.3179.45 Dependencies (appium): Version: [2.17.1,) Recommended Version: 2.17.1 - windows Version: [3.1.1,) Recommended Version: 3.1.1 - xcuitest Version: [7.32.0,) Recommended Version: 7.32.0 - mac2 Version: [1.20.3,) Recommended Version: 1.20.3 - uiautomator2 Version: [4.2.1,) Recommended Version: 4.2.1 The output could be further improved, if we added `desc` elements in `WorkloadDependencies.json` for each dependency, we could print those out as well.
1 parent a1cbcc4 commit b77e4ed

17 files changed

+609
-2
lines changed

src/Cli/dotnet/Commands/CliCommandStrings.resx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2434,6 +2434,18 @@ To display a value, specify the corresponding command-line option without provid
24342434
<data name="WorkloadManifestVersionColumn" xml:space="preserve">
24352435
<value>Manifest Version</value>
24362436
</data>
2437+
<data name="WorkloadDependenciesColumn" xml:space="preserve">
2438+
<value>Dependencies</value>
2439+
</data>
2440+
<data name="WorkloadDependencyVersion" xml:space="preserve">
2441+
<value>Version</value>
2442+
</data>
2443+
<data name="WorkloadDependencyRecommendedVersion" xml:space="preserve">
2444+
<value>Recommended Version</value>
2445+
</data>
2446+
<data name="WorkloadDependencyOptional" xml:space="preserve">
2447+
<value>optional</value>
2448+
</data>
24372449
<data name="WorkloadNotInstalled" xml:space="preserve">
24382450
<value>Couldn't find workload ID(s): {0}</value>
24392451
</data>

src/Cli/dotnet/Commands/Workload/WorkloadCommandDefinition.cs

Lines changed: 186 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
using System.CommandLine;
55
using System.CommandLine.Invocation;
6+
using System.Runtime.InteropServices;
7+
using System.Text.Json;
8+
using Microsoft.DotNet.Cli.CommandLine;
69
using Microsoft.DotNet.Cli.Commands.Workload.Clean;
710
using Microsoft.DotNet.Cli.Commands.Workload.Config;
811
using Microsoft.DotNet.Cli.Commands.Workload.Elevate;
@@ -18,9 +21,8 @@
1821
using Microsoft.DotNet.Cli.Utils;
1922
using Microsoft.NET.Sdk.WorkloadManifestReader;
2023
using Microsoft.TemplateEngine.Cli.Commands;
21-
using IReporter = Microsoft.DotNet.Cli.Utils.IReporter;
2224
using Command = System.CommandLine.Command;
23-
using Microsoft.DotNet.Cli.CommandLine;
25+
using IReporter = Microsoft.DotNet.Cli.Utils.IReporter;
2426

2527
namespace Microsoft.DotNet.Cli.Commands.Workload;
2628

@@ -119,6 +121,9 @@ void WriteUpdateModeAndAnyError(string indent = "")
119121
reporter.Write($"{separator}{CliCommandStrings.WorkloadInstallTypeColumn}:");
120122
reporter.WriteLine($" {WorkloadInstallType.GetWorkloadInstallType(new SdkFeatureBand(Product.Version), dotnetPath).ToString(),align}"
121123
);
124+
125+
PrintWorkloadDependencies(workloadManifest.ManifestPath, separator, reporter);
126+
122127
reporter.WriteLine("");
123128
}
124129
}
@@ -130,6 +135,185 @@ void WriteUpdateModeAndAnyError(string indent = "")
130135
}
131136
}
132137

138+
private static void PrintWorkloadDependencies(string manifestPath, string separator, IReporter reporter)
139+
{
140+
var manifestDir = Path.GetDirectoryName(manifestPath);
141+
if (manifestDir == null)
142+
{
143+
return;
144+
}
145+
146+
var dependenciesPath = Path.Combine(manifestDir, "WorkloadDependencies.json");
147+
if (!File.Exists(dependenciesPath))
148+
{
149+
return;
150+
}
151+
152+
try
153+
{
154+
var json = File.ReadAllText(dependenciesPath);
155+
using var doc = JsonDocument.Parse(json);
156+
157+
// The root object has workload manifest IDs as keys (e.g., "microsoft.net.sdk.android")
158+
foreach (var manifestEntry in doc.RootElement.EnumerateObject())
159+
{
160+
// Each manifest entry contains dependency categories (e.g., "workload", "jdk", "androidsdk")
161+
foreach (var category in manifestEntry.Value.EnumerateObject())
162+
{
163+
// Skip the "workload" category as it's metadata, not a dependency
164+
if (category.Name.Equals("workload", StringComparison.OrdinalIgnoreCase))
165+
{
166+
continue;
167+
}
168+
169+
reporter.WriteLine($"{separator}{CliCommandStrings.WorkloadDependenciesColumn} ({category.Name}):");
170+
PrintDependencyCategory(category.Value, separator + " ", reporter);
171+
}
172+
}
173+
}
174+
catch (JsonException)
175+
{
176+
// Silently ignore malformed JSON
177+
}
178+
catch (IOException)
179+
{
180+
// Silently ignore file read errors
181+
}
182+
}
183+
184+
private static void PrintDependencyCategory(JsonElement category, string indent, IReporter reporter)
185+
{
186+
foreach (var prop in category.EnumerateObject())
187+
{
188+
if (prop.Value.ValueKind == JsonValueKind.Array)
189+
{
190+
// Handle any array of items (packages, drivers, etc.)
191+
foreach (var item in prop.Value.EnumerateArray())
192+
{
193+
PrintItemInfo(item, indent, reporter);
194+
}
195+
}
196+
else
197+
{
198+
// Handle simple properties like "version", "recommendedVersion"
199+
var value = GetJsonValueAsString(prop.Value);
200+
if (!string.IsNullOrEmpty(value))
201+
{
202+
var displayName = GetLocalizedPropertyName(prop.Name);
203+
reporter.WriteLine($"{indent}{displayName}: {value}");
204+
}
205+
}
206+
}
207+
}
208+
209+
private static string GetLocalizedPropertyName(string propertyName)
210+
{
211+
return propertyName.ToLowerInvariant() switch
212+
{
213+
"version" => CliCommandStrings.WorkloadDependencyVersion,
214+
"recommendedversion" => CliCommandStrings.WorkloadDependencyRecommendedVersion,
215+
_ => propertyName
216+
};
217+
}
218+
219+
private static void PrintItemInfo(JsonElement item, string indent, IReporter reporter)
220+
{
221+
string? displayName = null;
222+
string? id = null;
223+
string? version = null;
224+
string? recommendedVersion = null;
225+
bool? optional = null;
226+
227+
foreach (var prop in item.EnumerateObject())
228+
{
229+
switch (prop.Name.ToLowerInvariant())
230+
{
231+
case "desc":
232+
case "name":
233+
displayName = GetJsonValueAsString(prop.Value);
234+
break;
235+
case "id":
236+
id = GetJsonValueAsString(prop.Value);
237+
break;
238+
case "version":
239+
version = GetJsonValueAsString(prop.Value);
240+
break;
241+
case "recommendedversion":
242+
recommendedVersion = GetJsonValueAsString(prop.Value);
243+
break;
244+
case "optional":
245+
optional = prop.Value.ValueKind == JsonValueKind.True ||
246+
string.Equals(prop.Value.ToString(), bool.TrueString, StringComparison.OrdinalIgnoreCase);
247+
break;
248+
case "sdkpackage":
249+
foreach (var sdkProp in prop.Value.EnumerateObject())
250+
{
251+
if (sdkProp.Name.Equals("id", StringComparison.OrdinalIgnoreCase))
252+
{
253+
id = GetJsonValueAsString(sdkProp.Value);
254+
}
255+
else if (sdkProp.Name.Equals("recommendedVersion", StringComparison.OrdinalIgnoreCase))
256+
{
257+
recommendedVersion = GetJsonValueAsString(sdkProp.Value);
258+
}
259+
}
260+
break;
261+
}
262+
}
263+
264+
if (!string.IsNullOrEmpty(displayName))
265+
{
266+
var optionalText = optional == true ? $" ({CliCommandStrings.WorkloadDependencyOptional})" : "";
267+
reporter.WriteLine($"{indent}- {displayName}{optionalText}");
268+
if (!string.IsNullOrEmpty(id))
269+
{
270+
reporter.WriteLine($"{indent} {id}");
271+
}
272+
if (!string.IsNullOrEmpty(version))
273+
{
274+
reporter.WriteLine($"{indent} {CliCommandStrings.WorkloadDependencyVersion}: {version}");
275+
}
276+
if (!string.IsNullOrEmpty(recommendedVersion))
277+
{
278+
reporter.WriteLine($"{indent} {CliCommandStrings.WorkloadDependencyRecommendedVersion}: {recommendedVersion}");
279+
}
280+
}
281+
}
282+
283+
private static string? GetJsonValueAsString(JsonElement element)
284+
{
285+
return element.ValueKind switch
286+
{
287+
JsonValueKind.String => element.GetString(),
288+
JsonValueKind.Number => element.GetRawText(),
289+
JsonValueKind.True => bool.TrueString,
290+
JsonValueKind.False => bool.FalseString,
291+
JsonValueKind.Object => GetPlatformSpecificId(element),
292+
_ => null
293+
};
294+
}
295+
296+
private static string? GetPlatformSpecificId(JsonElement element)
297+
{
298+
// Handle platform-specific IDs like:
299+
// "id": { "win-x64": "...", "mac-arm64": "...", ... }
300+
var rid = RuntimeInformation.RuntimeIdentifier;
301+
302+
// Try exact match first
303+
if (element.TryGetProperty(rid, out var exactMatch))
304+
{
305+
return GetJsonValueAsString(exactMatch);
306+
}
307+
308+
// Fall back to first available
309+
foreach (var prop in element.EnumerateObject())
310+
{
311+
return $"{GetJsonValueAsString(prop.Value)} ({prop.Name})";
312+
}
313+
314+
return null;
315+
}
316+
133317
public static Command Create()
134318
{
135319
Command command = new("workload", CliCommandStrings.WorkloadCommandDescription)

src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf

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

src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf

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

src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf

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

src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf

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

src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf

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

src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf

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

0 commit comments

Comments
 (0)