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 @@ -112,7 +112,7 @@ public DependencyContextBuilder(
runtimeIdentifier,
string.IsNullOrWhiteSpace(platformLibraryName));

_isPortable = _isFrameworkDependent && string.IsNullOrEmpty(_runtimeIdentifier);
_isPortable = _isFrameworkDependent && (string.IsNullOrEmpty(_runtimeIdentifier) || _runtimeIdentifier == "any");

if (_isFrameworkDependent != true || _isPortable != true)
{
Expand Down Expand Up @@ -317,7 +317,7 @@ public DependencyContext Build(string[] userRuntimeAssemblies = null)
* 1. If runtimeAssemblyGroups, nativeLibraryGroups, dependencies, and resourceAssemblies are all empty, remove this runtimeLibrary as well as any dependencies on it.
* 2. Add all runtimeLibraries to a list of to-be-processed libraries called libraryCandidatesForRemoval
* 3. libraryCandidatesForRemoval.Pop() --> if there are no runtimeAssemblyGroups, nativeLibraryGroups, or resourceAssemblies, and either dependencies is empty or all
* dependencies have something else that depends on them, remove it (and from libraryCandidatesForRemoval), adding everything that depends on this to
* dependencies have something else that depends on them, remove it (and from libraryCandidatesForRemoval), adding everything that depends on this to
* libraryCandidatesForRemoval if it isn't already there
* Repeat 3 until libraryCandidatesForRemoval is empty
*/
Expand Down Expand Up @@ -483,8 +483,8 @@ public DependencyContext Build(string[] userRuntimeAssemblies = null)
runtimeSignature: string.Empty,
_isPortable);

// Compute the runtime fallback graph
//
// Compute the runtime fallback graph
//
// If the input RuntimeGraph is empty, or we're not compiling
// for a specific RID, then an runtime fallback graph is empty
//
Expand Down
11 changes: 8 additions & 3 deletions src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ public class GenerateDepsFile : TaskBase

public string RuntimeIdentifier { get; set; }

/// <summary>
/// Strips the RID if it's any, because that's not reasonable
/// </summary>
private string EffectiveRuntimeIdentifier => string.IsNullOrEmpty(RuntimeIdentifier) ? null : RuntimeIdentifier == "any" ? null : RuntimeIdentifier;

public string PlatformLibraryName { get; set; }

public ITaskItem[] RuntimeFrameworks { get; set; }
Expand Down Expand Up @@ -95,7 +100,7 @@ public class GenerateDepsFile : TaskBase

public bool IncludeProjectsNotInAssetsFile { get; set; }

// List of runtime identifer (platform part only) to validate for runtime assets
// List of runtime identifier (platform part only) to validate for runtime assets
// If set, the task will warn on any RIDs that aren't in the list
public string[] ValidRuntimeIdentifierPlatformsForAssets { get; set; }

Expand Down Expand Up @@ -137,7 +142,7 @@ private void WriteDepsFile(string depsFilePath)
LockFile lockFile = new LockFileCache(this).GetLockFile(AssetsFilePath);
projectContext = lockFile.CreateProjectContext(
TargetFramework,
RuntimeIdentifier,
EffectiveRuntimeIdentifier,
PlatformLibraryName,
RuntimeFrameworks,
IsSelfContained);
Expand Down Expand Up @@ -226,7 +231,7 @@ bool ShouldIncludeRuntimeAsset(ITaskItem item)
RuntimeFrameworks,
isSelfContained: IsSelfContained,
platformLibraryName: PlatformLibraryName,
runtimeIdentifier: RuntimeIdentifier,
runtimeIdentifier: EffectiveRuntimeIdentifier,
targetFramework: TargetFramework);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Tasks/Microsoft.NET.Build.Tasks/LockFileExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public static ProjectContext CreateProjectContext(
public static bool IsFrameworkDependent(ITaskItem[] runtimeFrameworks, bool isSelfContained, string runtimeIdentifier, bool hasPlatformLibrary)
{
return (hasPlatformLibrary || runtimeFrameworks?.Any() == true) &&
(!isSelfContained || string.IsNullOrEmpty(runtimeIdentifier));
(!isSelfContained || (string.IsNullOrEmpty(runtimeIdentifier) || runtimeIdentifier == "any"));
}

public static LockFileTargetLibrary GetLibrary(this LockFileTarget lockFileTarget, string libraryName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ out List<KnownRuntimePack> knownRuntimePacksForTargetFramework
var runtimeRequiredByDeployment
= (SelfContained || ReadyToRunEnabled) &&
!string.IsNullOrEmpty(RuntimeIdentifier) &&
RuntimeIdentifier != "any" &&
selectedRuntimePack != null &&
Copy link
Member

Choose a reason for hiding this comment

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

In DependencyContextBuilder, I wonder if we could skip the check for nativeRuntimeTargetsFiles if the RID is Any?

Copy link
Member Author

Choose a reason for hiding this comment

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

I didn't quite understand what you meant here, but I did see a related change that we needed to make around a check on _isPortable that I've updated too.

!string.IsNullOrEmpty(selectedRuntimePack.Value.RuntimePackNamePatterns);
Copy link
Member

Choose a reason for hiding this comment

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

I'm surprised GenerateBundle seems to fail with Any rid. Is there a chance that ever gets called? Likely, the answer is no.

Copy link
Member Author

Choose a reason for hiding this comment

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

GenerateBundle is only called for single-file publishing, and I believe that the combination of any + singlefile is blocked higher up the stack. Good call-out!


Expand Down Expand Up @@ -372,6 +373,13 @@ var runtimeRequiredByDeployment
continue;
}

if (runtimeIdentifier == "any")
{
// The `any` RID represents a platform-agnostic target. As such, it has no
// platform-specific runtime pack associated with it.
continue;
}

// Pass in null for the runtimePacks list, as for these runtime identifiers we only want to
// download the runtime packs, but not use the assets from them
ProcessRuntimeIdentifier(runtimeIdentifier, runtimePackForRuntimeIDProcessing, runtimePackVersion, additionalFrameworkReferencesForRuntimePack: null,
Expand Down
7 changes: 7 additions & 0 deletions src/Tasks/Microsoft.NET.Build.Tasks/ResolveAppHosts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,13 @@ protected override void ExecuteCore()
{
foreach (var otherRuntimeIdentifier in OtherRuntimeIdentifiers)
{
// The 'any' RID represents a platform-agnostic platform. As such, it has no
// apphost pack associated with it.
if (otherRuntimeIdentifier == "any")
{
continue;
}

// Download any apphost packages for other runtime identifiers.
// This allows you to specify the list of RIDs in RuntimeIdentifiers and only restore once,
// and then build for each RuntimeIdentifier without restoring separately.
Expand Down
54 changes: 49 additions & 5 deletions test/Microsoft.DotNet.PackageInstall.Tests/EndToEndToolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,9 @@ public void PackageToolWithAnyRid()
.And.Satisfy<string>(EnsurePackageIsFdd);

// top-level package should declare all of the rids
var topLevelPackage = packages.First(p => p.EndsWith($"{packageIdentifier}.{toolSettings.ToolPackageVersion}.nupkg"));
var settingsXml = GetToolSettingsFile(topLevelPackage);
var packageNodes = GetRidsInSettingsFile(settingsXml);

packageNodes.Should().BeEquivalentTo([.. expectedRids, "any"], "The top-level package should declare all of the RIDs for the tools it contains");
var topLevelPackage = packages.FirstOrDefault(p => p.EndsWith($"{packageIdentifier}.{toolSettings.ToolPackageVersion}.nupkg"));
topLevelPackage.Should().NotBeNull($"Package {packageIdentifier}.{toolSettings.ToolPackageVersion}.nupkg should be present in the tool packages directory")
.And.Satisfy<string>(SupportAllOfTheseRuntimes([.. expectedRids, "any"]));
}

[Fact]
Expand Down Expand Up @@ -353,6 +351,45 @@ public void StripsPackageTypesFromInnerToolPackages()
foundRids.Should().BeEquivalentTo(expectedRids, "The top-level package should declare all of the RIDs for the tools it contains");
}

[Fact]
public void MixedPackageTypesBuildInASingleBatchSuccessfully()
{
var toolSettings = new TestToolBuilder.TestToolSettings()
{
RidSpecific = true,
IncludeAnyRid = true,
SelfContained = true // ensure that the RID-specific packages get runtime packs/assets - but the any RID package does not!
};
string toolPackagesPath = ToolBuilder.CreateTestTool(Log, toolSettings, collectBinlogs: true);

var packages = Directory.GetFiles(toolPackagesPath, "*.nupkg");
var packageIdentifier = toolSettings.ToolPackageId;
var ridSpecificPackages = ToolsetInfo.LatestRuntimeIdentifiers.Split(';');
packages.Length.Should().Be(ridSpecificPackages.Length + 1 + 1, "There should be one package for the tool-wrapper and one for each RID, and one for the any rid");
foreach (string rid in ridSpecificPackages)
{
var packageName = $"{toolSettings.ToolPackageId}.{rid}.{toolSettings.ToolPackageVersion}";
var package = packages.FirstOrDefault(p => p.EndsWith(packageName + ".nupkg"));
package.Should()
.NotBeNull($"Package {packageName} should be present in the tool packages directory")
.And.Satisfy<string>(EnsurePackageIsAnExecutable)
.And.Satisfy<string>(EnsurePackageOnlyHasToolRidPackageType);
}

var agnosticFallbackPackageId = $"{toolSettings.ToolPackageId}.any.{toolSettings.ToolPackageVersion}";
var agnosticFallbackPackage = packages.FirstOrDefault(p => p.EndsWith(agnosticFallbackPackageId + ".nupkg"));
agnosticFallbackPackage.Should()
.NotBeNull($"Package {agnosticFallbackPackageId} should be present in the tool packages directory")
.And.Satisfy<string>(EnsurePackageIsFdd)
.And.Satisfy<string>(EnsurePackageOnlyHasToolRidPackageType);

// top-level package should declare all of the rids
var topLevelPackage = packages.First(p => p.EndsWith($"{packageIdentifier}.{toolSettings.ToolPackageVersion}.nupkg"));
topLevelPackage.Should().NotBeNull($"Package {packageIdentifier}.{toolSettings.ToolPackageVersion}.nupkg should be present in the tool packages directory")
.And.Satisfy<string>(EnsurePackageHasNoRunner)
.And.Satisfy(SupportAllOfTheseRuntimes([..ridSpecificPackages, "any"]));
}

private Action<string> EnsurePackageHasToolPackageTypeAnd(string[] additionalPackageTypes) => (string packagePath) =>
{
var nuspec = GetPackageNuspec(packagePath);
Expand All @@ -363,6 +400,13 @@ private Action<string> EnsurePackageHasToolPackageTypeAnd(string[] additionalPac
.And.BeEquivalentTo(expectedPackageTypes, "The PackageType should be 'DotnetTool'.");
};

private Action<string> SupportAllOfTheseRuntimes(string[] runtimes) => (string packagePath) =>
{
var settingsXml = GetToolSettingsFile(packagePath);
var rids = GetRidsInSettingsFile(settingsXml);
rids.Should().BeEquivalentTo(runtimes, "The tool settings file should contain all of the specified RuntimeIdentifierPackage elements.");
};

static void EnsurePackageOnlyHasToolRidPackageType(string packagePath)
{
var nuspec = GetPackageNuspec(packagePath);
Expand Down
18 changes: 17 additions & 1 deletion test/Microsoft.DotNet.PackageInstall.Tests/TestToolBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,28 @@ public class TestToolSettings
public string ToolPackageVersion { get; set; } = "1.0.0";
public string ToolCommandName { get; set; } = "TestTool";
public string[]? AdditionalPackageTypes { get; set; } = null;

public bool NativeAOT { get; set { field = value; this.RidSpecific = value; } } = false;
public bool SelfContained { get; set { field = value; this.RidSpecific = value; } } = false;
public bool Trimmed { get; set { field = value; this.RidSpecific = value; } } = false;

/// <summary>
/// If set, the generated tool will include the <c>any</c> RID in the list of RIDs to target.
/// This will cause a framework-dependent, platform-agnostic package to be created.
/// </summary>
public bool IncludeAnyRid { get; set { field = value; } } = false;

/// <summary>
/// If set, the generated tool will target all of the RIDs specified in <see cref="ToolsetInfo.LatestRuntimeIdentifiers"/>.
/// Defaults to <see langword="false"/>.
/// </summary>
public bool RidSpecific { get; set; } = false;

/// <summary>
/// If set, the generated tool will include the current executing platform's RID in the list of RIDs to target
/// (which is otherwise made of <see cref="ToolsetInfo.LatestRuntimeIdentifiers"/>.) If set to <see langword="false"/>,
/// the current RID will be stripped from that set.
/// Defaults to <see langword="true"/>.
/// </summary>
public bool IncludeCurrentRid { get; set; } = true;

public string GetIdentifier() {
Expand Down
Loading