Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO;
using System.Linq;
using Microsoft.DotNet.Cli;
using Microsoft.NET.Sdk.Localization;

namespace Microsoft.NET.Sdk.WorkloadManifestReader
{
Expand All @@ -16,6 +17,7 @@ public class SdkDirectoryWorkloadManifestProvider : IWorkloadManifestProvider
private readonly string [] _manifestDirectories;
private static HashSet<string> _outdatedManifestIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "microsoft.net.workload.android", "microsoft.net.workload.blazorwebassembly", "microsoft.net.workload.ios",
"microsoft.net.workload.maccatalyst", "microsoft.net.workload.macos", "microsoft.net.workload.tvos" };
private readonly HashSet<string>? _knownManifestIds;

public SdkDirectoryWorkloadManifestProvider(string sdkRootPath, string sdkVersion)
: this(sdkRootPath, sdkVersion, Environment.GetEnvironmentVariable)
Expand Down Expand Up @@ -52,6 +54,12 @@ static int Last2DigitsTo0(int versionBuild)
_sdkRootPath = sdkRootPath;
_sdkVersionBand = sdkVersionBand;

var knownManifestIdsFilePath = Path.Combine(_sdkRootPath, "sdk", sdkVersion, "IncludedWorkloadManifests.txt");
if (File.Exists(knownManifestIdsFilePath))
{
_knownManifestIds = File.ReadAllLines(knownManifestIdsFilePath).Where(l => !string.IsNullOrEmpty(l)).ToHashSet();
}

var manifestDirectory = Path.Combine(_sdkRootPath, "sdk-manifests", _sdkVersionBand);

var manifestDirectoryEnvironmentVariable = getEnvironmentVariable(EnvironmentVariableNames.WORKLOAD_MANIFEST_ROOTS);
Expand Down Expand Up @@ -81,6 +89,7 @@ static int Last2DigitsTo0(int versionBuild)

public IEnumerable<string> GetManifestDirectories()
Copy link

@wli3 wli3 Aug 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implementation side, we just need to make sure GetManifestDirectories and GetManifests are the only methods used to search for manifest. Or other callers might get the old behavior

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I think that's the intention regardless of this change.

{
var manifestIdsToDirectories = new Dictionary<string, string>();
if (_manifestDirectories.Length == 1)
{
// Optimization for common case where test hook to add additional directories isn't being used
Expand All @@ -90,7 +99,7 @@ public IEnumerable<string> GetManifestDirectories()
{
if (!IsManifestIdOutdated(workloadManifestDirectory))
{
yield return workloadManifestDirectory;
manifestIdsToDirectories.Add(Path.GetFileName(workloadManifestDirectory), workloadManifestDirectory);
}
}
}
Expand All @@ -114,10 +123,42 @@ public IEnumerable<string> GetManifestDirectories()
{
if (!IsManifestIdOutdated(workloadManifestDirectory))
{
yield return workloadManifestDirectory;
manifestIdsToDirectories.Add(Path.GetFileName(workloadManifestDirectory), workloadManifestDirectory);
}
}
}

if (_knownManifestIds != null && _knownManifestIds.Any(id => !manifestIdsToDirectories.ContainsKey(id)))
{
var missingManifestIds = _knownManifestIds.Where(id => !manifestIdsToDirectories.ContainsKey(id));
foreach (var missingManifestId in missingManifestIds)
{
var manifestDir = FallbackForMissingManifest(missingManifestId);
manifestIdsToDirectories.Add(missingManifestId, manifestDir);
}
}

return manifestIdsToDirectories.Values;
}

private string FallbackForMissingManifest(string manifestId)
{
var candidateFeatureBands = Directory.GetDirectories(Path.Combine(_sdkRootPath, "sdk-manifests"))
.Select(dir => Path.GetFileName(dir))
.Where(featureBand => Version.TryParse(featureBand, out _))
.Select(featureBand => Version.Parse(featureBand))
.Where(featureBand => featureBand < Version.Parse(_sdkVersionBand));
var matchingManifestFatureBands = candidateFeatureBands
.Where(featureBand => Directory.Exists(Path.Combine(_sdkRootPath, "sdk-manifests", featureBand.ToString(), manifestId)));
if (matchingManifestFatureBands.Any())
{
return Path.Combine(_sdkRootPath, "sdk-manifests", matchingManifestFatureBands.Max()!.ToString(), manifestId);
}
else
{
// Manifest does not exist
return string.Empty;
}
}

private bool IsManifestIdOutdated(string workloadManifestDir)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;

using System.Text.Json;
using FluentAssertions;
using Microsoft.DotNet.Cli;
using Microsoft.NET.Sdk.WorkloadManifestReader;
Expand Down Expand Up @@ -247,6 +247,52 @@ var sdkDirectoryWorkloadManifestProvider
.BeEquivalentTo("iOSContent");
}

[Fact]
public void ItShouldFallbackWhenFeatureBandHasNoManifests()
{
var testDirectory = _testAssetsManager.CreateTestDirectory().Path;
var fakeDotnetRootDirectory = Path.Combine(testDirectory, "dotnet");

// Write 4.0.100 manifests-> android only
var manifestDirectory4 = Path.Combine(fakeDotnetRootDirectory, "sdk-manifests", "4.0.100");
Directory.CreateDirectory(manifestDirectory4);
Directory.CreateDirectory(Path.Combine(manifestDirectory4, "Android"));
File.WriteAllText(Path.Combine(manifestDirectory4, "Android", "WorkloadManifest.json"), "4.0.100");

// Write 5.0.100 manifests-> ios and android
var manifestDirectory5 = Path.Combine(fakeDotnetRootDirectory, "sdk-manifests", "5.0.100");
Directory.CreateDirectory(manifestDirectory5);
Directory.CreateDirectory(Path.Combine(manifestDirectory5, "Android"));
File.WriteAllText(Path.Combine(manifestDirectory5, "Android", "WorkloadManifest.json"), "5.0.100");
Directory.CreateDirectory(Path.Combine(manifestDirectory5, "iOS"));
File.WriteAllText(Path.Combine(manifestDirectory5, "iOS", "WorkloadManifest.json"), "5.0.100");

// Write 6.0.100 manifests-> ios only
var manifestDirectory6 = Path.Combine(fakeDotnetRootDirectory, "sdk-manifests", "6.0.100");
Directory.CreateDirectory(manifestDirectory6);
Directory.CreateDirectory(Path.Combine(manifestDirectory6, "iOS"));
File.WriteAllText(Path.Combine(manifestDirectory6, "iOS", "WorkloadManifest.json"), "6.0.100");

// Write 7.0.100 manifests-> ios and android
var manifestDirectory7 = Path.Combine(fakeDotnetRootDirectory, "sdk-manifests", "7.0.100");
Directory.CreateDirectory(manifestDirectory7);
Directory.CreateDirectory(Path.Combine(manifestDirectory7, "Android"));
File.WriteAllText(Path.Combine(manifestDirectory7, "Android", "WorkloadManifest.json"), "7.0.100");
Directory.CreateDirectory(Path.Combine(manifestDirectory7, "iOS"));
File.WriteAllText(Path.Combine(manifestDirectory7, "iOS", "WorkloadManifest.json"), "7.0.100");

var knownWorkloadsFilePath = Path.Combine(fakeDotnetRootDirectory, "sdk", "6.0.100", "IncludedWorkloadManifests.txt");
Directory.CreateDirectory(Path.GetDirectoryName(knownWorkloadsFilePath)!);
File.WriteAllText(knownWorkloadsFilePath, "Android\niOS");

var sdkDirectoryWorkloadManifestProvider
= new SdkDirectoryWorkloadManifestProvider(sdkRootPath: fakeDotnetRootDirectory, sdkVersion: "6.0.100");

GetManifestContents(sdkDirectoryWorkloadManifestProvider)
.Should()
.BeEquivalentTo("6.0.100", "5.0.100");
}

private IEnumerable<string> GetManifestContents(SdkDirectoryWorkloadManifestProvider manifestProvider)
{
return manifestProvider.GetManifests().Select(manifest => new StreamReader(manifest.openManifestStream()).ReadToEnd());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using Microsoft.NET.TestFramework.Assertions;
using Microsoft.Extensions.EnvironmentAbstractions;
using Microsoft.DotNet.Cli.Utils;
using System.Text.Json;

namespace Microsoft.DotNet.Cli.Workload.Install.Tests
{
Expand Down