Skip to content

Enable "portable" RID graph when targeting .NET 8 and higher #34279

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 4, 2023
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
464 changes: 464 additions & 0 deletions src/Layout/redist/PortableRuntimeIdentifierGraph.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/Layout/redist/targets/BuildToolsetTasks.targets
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@
<UsingTask TaskName="ZipFileCreateFromDirectory" AssemblyFile="$(ToolsetTaskDll)"/>
<UsingTask TaskName="OverrideAndCreateBundledNETCoreAppPackageVersion" AssemblyFile="$(ToolsetTaskDll)"/>
<UsingTask TaskName="OverrideWasmRuntimePackVersions" AssemblyFile="$(ToolsetTaskDll)"/>
<UsingTask TaskName="UpdatePortableRuntimeIdentifierGraph" AssemblyFile="$(ToolsetTaskDll)"/>

</Project>
12 changes: 12 additions & 0 deletions src/Layout/redist/targets/GenerateLayout.targets
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
<Project>

<Target Name="PublishPortableRuntimeIdentifierGraph"
BeforeTargets="Build">

<UpdatePortableRuntimeIdentifierGraph
InputFile="PortableRuntimeIdentifierGraph.json"
OutputFile="$(OutputPath)/PortableRuntimeIdentifierGraph.json"
AdditionalRuntimeIdentifiers="@(AdditionalRuntimeIdentifier)"
/>

</Target>

<Target Name="PublishVersionFile"
BeforeTargets="Build">

Expand Down
60 changes: 60 additions & 0 deletions src/Layout/toolset-tasks/UpdatePortableRuntimeIdentifierGraph.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Microsoft.DotNet.Cli.Build
{
public class UpdatePortableRuntimeIdentifierGraph : Task
{
[Required]
public string InputFile { get; set; }

[Required]
public string OutputFile { get; set; }


// ItemSpec should be a RID, and "Imports" metadata should be a semicolon-separated list of RIDs that the ItemSpec RID imports
public ITaskItem[] AdditionalRuntimeIdentifiers { get; set; }

public override bool Execute()
{
JToken json;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we have a "fast path" here for if AdditionalRuntimeIdentifiers is null? Then it should just be a copy, right?


using (var file = File.OpenText(InputFile))
using (JsonTextReader reader = new JsonTextReader(file))
{
json = JObject.ReadFrom(reader);
}

JObject runtimes = (JObject) json["runtimes"];

if (AdditionalRuntimeIdentifiers != null)
{
foreach (var rid in AdditionalRuntimeIdentifiers)
{
var importedRids = rid.GetMetadata("Imports").Split(';');
runtimes.Add(rid.ItemSpec, new JObject(new JProperty("#import", new JArray(importedRids))));
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is correct and updates json["runtimes"] rather than making a copy...but can we have a regression test for that? Or perhaps just add json["runtimes"] = runtimes at the end?

}
}

using (var file = File.CreateText(OutputFile))
using (var writer = new JsonTextWriter(file) { Formatting = Formatting.Indented })
{
json.WriteTo(writer);
}

return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ Copyright (c) .NET Foundation. All rights reserved.
<ResolveReadyToRunCompilers RuntimePacks="@(ResolvedRuntimePack)"
Crossgen2Packs="@(ResolvedCrossgen2Pack)"
TargetingPacks="@(ResolvedTargetingPack)"
RuntimeGraphPath="$(BundledRuntimeIdentifierGraphFile)"
RuntimeGraphPath="$(RuntimeIdentifierGraphPath)"
NETCoreSdkRuntimeIdentifier="$(NETCoreSdkRuntimeIdentifier)"
EmitSymbols="$(PublishReadyToRunEmitSymbols)"
ReadyToRunUseCrossgen2="$(PublishReadyToRunUseCrossgen2)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ Copyright (c) .NET Foundation. All rights reserved.
ResolvedNuGetFiles="@(NativeCopyLocalItems);@(ResourceCopyLocalItems);@(RuntimeCopyLocalItems)"
ResolvedRuntimeTargetsFiles="@(RuntimeTargetsCopyLocalItems)"
TargetFramework="$(TargetFramework)"
RuntimeGraphPath="$(BundledRuntimeIdentifierGraphFile)"
RuntimeGraphPath="$(RuntimeIdentifierGraphPath)"
IncludeProjectsNotInAssetsFile="$(IncludeProjectsNotInAssetsFileInDepsFile)"
/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ Copyright (c) .NET Foundation. All rights reserved.
<ResolveReadyToRunCompilers RuntimePacks="@(ResolvedRuntimePack)"
Crossgen2Packs="@(ResolvedCrossgen2Pack)"
TargetingPacks="@(ResolvedTargetingPack)"
RuntimeGraphPath="$(BundledRuntimeIdentifierGraphFile)"
RuntimeGraphPath="$(RuntimeIdentifierGraphPath)"
NETCoreSdkRuntimeIdentifier="$(NETCoreSdkRuntimeIdentifier)"
EmitSymbols="$(PublishReadyToRunEmitSymbols)"
ReadyToRunUseCrossgen2="$(PublishReadyToRunUseCrossgen2)"
Expand Down Expand Up @@ -1013,7 +1013,7 @@ Copyright (c) .NET Foundation. All rights reserved.
<_GeneratePublishDependencyFilePropertyInputsCacheToHash Include="$(MicrosoftNETPlatformLibrary)" />
<_GeneratePublishDependencyFilePropertyInputsCacheToHash Include="$(SelfContained)" />
<_GeneratePublishDependencyFilePropertyInputsCacheToHash Include="$(IncludeFileVersionsInDependencyFile)" />
<_GeneratePublishDependencyFilePropertyInputsCacheToHash Include="$(BundledRuntimeIdentifierGraphFile)" />
<_GeneratePublishDependencyFilePropertyInputsCacheToHash Include="$(RuntimeIdentifierGraphPath)" />
<_GeneratePublishDependencyFilePropertyInputsCacheToHash Include="$(IncludeProjectsNotInAssetsFileInDepsFile)" />
</ItemGroup>

Expand Down Expand Up @@ -1099,7 +1099,7 @@ Copyright (c) .NET Foundation. All rights reserved.
IsSelfContained="$(SelfContained)"
IsSingleFile="$(_IsSingleFilePublish)"
IncludeRuntimeFileVersions="$(IncludeFileVersionsInDependencyFile)"
RuntimeGraphPath="$(BundledRuntimeIdentifierGraphFile)"
RuntimeGraphPath="$(RuntimeIdentifierGraphPath)"
IncludeProjectsNotInAssetsFile="$(IncludeProjectsNotInAssetsFileInDepsFile)"/>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ Copyright (c) .NET Foundation. All rights reserved.

<Import Project="$(NETCoreSdkBundledVersionsProps)" Condition="Exists('$(NETCoreSdkBundledVersionsProps)')" />

<PropertyGroup>
<!-- Set RuntimeIdentifier graph for NuGet (this needs to be after NETCoreSdkBundledVersionsProps is imported, as that's where
BundledRuntimeIdentifierGraphFile is set. -->
<RuntimeIdentifierGraphPath Condition="'$(RuntimeIdentifierGraphPath)' == ''">$(BundledRuntimeIdentifierGraphFile)</RuntimeIdentifierGraphPath>
</PropertyGroup>

<PropertyGroup>
<!-- Disable web SDK implicit package versions for ASP.NET packages, since the .NET SDK now handles that -->
<EnableWebSdkImplicitPackageVersions>false</EnableWebSdkImplicitPackageVersions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Copyright (c) .NET Foundation. All rights reserved.
TargetPlatformIdentifier="$(TargetPlatformIdentifier)"
TargetPlatformVersion="$(TargetPlatformVersion)"
TargetingPackRoot="$(NetCoreTargetingPackRoot)"
RuntimeGraphPath="$(BundledRuntimeIdentifierGraphFile)"
RuntimeGraphPath="$(RuntimeIdentifierGraphPath)"
SelfContained="$(SelfContained)"
ReadyToRunEnabled="$(PublishReadyToRun)"
ReadyToRunUseCrossgen2="$(PublishReadyToRunUseCrossgen2)"
Expand Down Expand Up @@ -155,7 +155,7 @@ Copyright (c) .NET Foundation. All rights reserved.
DotNetSingleFileHostExecutableNameWithoutExtension="$(_DotNetSingleFileHostExecutableNameWithoutExtension)"
DotNetComHostLibraryNameWithoutExtension="$(_DotNetComHostLibraryNameWithoutExtension)"
DotNetIjwHostLibraryNameWithoutExtension="$(_DotNetIjwHostLibraryNameWithoutExtension)"
RuntimeGraphPath="$(BundledRuntimeIdentifierGraphFile)"
RuntimeGraphPath="$(RuntimeIdentifierGraphPath)"
KnownAppHostPacks="@(KnownAppHostPack)"
NuGetRestoreSupported="$(_NuGetRestoreSupported)"
EnableAppHostPackDownload="$(EnableAppHostPackDownload)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ Copyright (c) .NET Foundation. All rights reserved.
<_GenerateSingleFileBundlePropertyInputsCache>$([MSBuild]::NormalizePath($(MSBuildProjectDirectory), $(_GenerateSingleFileBundlePropertyInputsCache)))</_GenerateSingleFileBundlePropertyInputsCache>
</PropertyGroup>

<!-- For .NET 8 and higher, we will by default use a simplified "portable" RID graph -->
<PropertyGroup Condition="'$(UseRidGraph)' == ''">
<UseRidGraph Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '8.0'))">false</UseRidGraph>
Copy link
Contributor

Choose a reason for hiding this comment

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

I was tripped up on this once and almost said this is incorrect when it's correct. UseRidGraph to me sounds like it means use the new thing, especially since the new thing is the "portable" rid graph...can we add portable to this name and flip its true/falseness? Or add full and not flip it?

<UseRidGraph Condition="'$(UseRidGraph)' == ''">true</UseRidGraph>
</PropertyGroup>

<PropertyGroup Condition="'$(RuntimeIdentifierGraphPath)' == ''">
<RuntimeIdentifierGraphPath Condition="'$(UseRidGraph)' == 'true'">$(BundledRuntimeIdentifierGraphFile)</RuntimeIdentifierGraphPath>

<!-- The portable RID graph should be in the same directory as the full RID graph -->
<RuntimeIdentifierGraphPath Condition="'$(UseRidGraph)' != 'true'">$([System.IO.Path]::GetDirectoryName($(BundledRuntimeIdentifierGraphFile)))/PortableRuntimeIdentifierGraph.json</RuntimeIdentifierGraphPath>
</PropertyGroup>

<ItemGroup>
<GenerateRuntimeConfigurationFilesInputs Include="$(ProjectAssetsFile)" />
<GenerateRuntimeConfigurationFilesInputs Include="$(ProjectAssetsCacheFile)" />
Expand Down Expand Up @@ -288,7 +301,7 @@ Copyright (c) .NET Foundation. All rights reserved.
ResolvedRuntimeTargetsFiles="@(RuntimeTargetsCopyLocalItems)"
IsSelfContained="$(SelfContained)"
IncludeRuntimeFileVersions="$(IncludeFileVersionsInDependencyFile)"
RuntimeGraphPath="$(BundledRuntimeIdentifierGraphFile)"
RuntimeGraphPath="$(RuntimeIdentifierGraphPath)"
IncludeProjectsNotInAssetsFile="$(IncludeProjectsNotInAssetsFileInDepsFile)"
ValidRuntimeIdentifierPlatformsForAssets="@(_ValidRuntimeIdentifierPlatformsForAssets)"/>
<ItemGroup>
Expand Down
66 changes: 66 additions & 0 deletions src/Tests/Microsoft.NET.Build.Tests/RuntimeIdentifierGraphTests.cs
Original file line number Diff line number Diff line change
@@ -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;
using FluentAssertions;
using Microsoft.NET.TestFramework;
using Microsoft.NET.TestFramework.Assertions;
using Microsoft.NET.TestFramework.Commands;
using Microsoft.NET.TestFramework.ProjectConstruction;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.NET.Build.Tests
{
public class RuntimeIdentifierGraphTests : SdkTest
{
public RuntimeIdentifierGraphTests(ITestOutputHelper log) : base(log)
{
}

[Theory]
[InlineData("net7.0", null, true)]
[InlineData("net8.0", null, false)]
[InlineData("net7.0", "false", false)]
[InlineData("net8.0", "true", true)]
public void ItUsesCorrectRuntimeIdentifierGraph(string targetFramework, string useRidGraphValue, bool shouldUseFullRidGraph)
{
var testProject = new TestProject()
{
TargetFrameworks = targetFramework,
IsExe = true
};

if (useRidGraphValue != null)
{
testProject.AdditionalProperties["UseRidGraph"] = useRidGraphValue;
}

testProject.RecordProperties("RuntimeIdentifierGraphPath");

var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: targetFramework + "_" + (useRidGraphValue ?? "null"));

var buildCommand = new BuildCommand(testAsset);

buildCommand.Execute()
.Should()
.Pass();

var runtimeIdentifierGraphPath = testProject.GetPropertyValues(testAsset.TestRoot)["RuntimeIdentifierGraphPath"];

if (shouldUseFullRidGraph)
{
Path.GetFileName(runtimeIdentifierGraphPath).Should().Be("RuntimeIdentifierGraph.json");
}
else
{
Path.GetFileName(runtimeIdentifierGraphPath).Should().Be("PortableRuntimeIdentifierGraph.json");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public void There_should_be_no_unresolved_conflicts()
Name = "CrossPublish",
TargetFrameworks = ToolsetInfo.CurrentTargetFramework,
IsExe = true,
RuntimeIdentifier = "centos.8-x64"
RuntimeIdentifier = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "linux-x64" : "win-x64"
};

testProject.PackageReferences.Add(new TestPackageReference("System.Threading", "4.3.0"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,6 @@ public static void Main()
}

[Theory]
[InlineData("win-arm")]
[InlineData("win8-arm")]
[InlineData("win81-arm")]
[InlineData($"{ToolsetInfo.LatestWinRuntimeIdentifier}-arm")]
[InlineData($"{ToolsetInfo.LatestWinRuntimeIdentifier}-arm64")]
public void Publish_standalone_post_netcoreapp2_arm_app(string runtimeIdentifier)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,17 @@ public GivenThatWeWantToPublishASingleFileApp(ITestOutputHelper log) : base(log)
{
}

private PublishCommand GetPublishCommand(string identifier = null, [CallerMemberName] string callingMethod = "")
private PublishCommand GetPublishCommand(string identifier = null, [CallerMemberName] string callingMethod = "", Action<XDocument> projectChanges = null)
{
if (projectChanges == null)
{
projectChanges = d => { };
}

var testAsset = _testAssetsManager
.CopyTestAsset(TestProjectName, callingMethod, identifier)
.WithSource();
.WithSource()
.WithProjectChanges(projectChanges);

// Create the following content:
// <TestRoot>/SmallNameDir/This is a directory with a really long name for one that only contains a small file/.word
Expand Down Expand Up @@ -127,7 +133,17 @@ public void It_errors_when_publishing_single_file_without_apphost()
public void It_generates_publishing_single_file_with_win7()
{
const string rid = "win7-x86";
GetPublishCommand()

// Retarget project to net7.0, as net8.0 and up by default use portable runtime graph which doesn't have win7-* RIDs
var projectChanges = (XDocument doc) =>
{
var ns = doc.Root.Name.Namespace;
doc.Root.Element(ns + "PropertyGroup")
.Element(ns + "TargetFramework")
.Value = "net7.0";
};

GetPublishCommand(projectChanges: projectChanges)
.Execute($"/p:RuntimeIdentifier={rid}", PublishSingleFile)
.Should()
.Pass();
Expand Down
8 changes: 4 additions & 4 deletions src/Tests/Microsoft.NET.TestFramework/ToolsetInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ public class ToolsetInfo
public const string NextTargetFramework = "net9.0";
public const string NextTargetFrameworkVersion = "9.0";

public const string LatestWinRuntimeIdentifier = "win10";
public const string LatestLinuxRuntimeIdentifier = "ubuntu.22.04";
public const string LatestMacRuntimeIdentifier = "osx.13";
public const string LatestRuntimeIdentifiers = $"{LatestWinRuntimeIdentifier}-x64;{LatestWinRuntimeIdentifier}-x86;osx.10.10-x64;osx.10.11-x64;osx.10.12-x64;osx.10.14-x64;{LatestMacRuntimeIdentifier}-x64;ubuntu.14.04-x64;ubuntu.16.04-x64;ubuntu.16.10-x64;ubuntu.18.04-x64;ubuntu.20.04-x64;{LatestLinuxRuntimeIdentifier}-x64;centos.9-x64;rhel.9-x64;debian.9-x64;fedora.37-x64;opensuse.42.3-x64;linux-musl-x64";
public const string LatestWinRuntimeIdentifier = "win";
public const string LatestLinuxRuntimeIdentifier = "linux";
public const string LatestMacRuntimeIdentifier = "osx";
public const string LatestRuntimeIdentifiers = $"{LatestWinRuntimeIdentifier}-x64;{LatestWinRuntimeIdentifier}-x86;osx-x64;{LatestMacRuntimeIdentifier}-x64;{LatestLinuxRuntimeIdentifier}-x64;linux-musl-x64";

public string DotNetRoot { get; }
public string DotNetHostPath { get; }
Expand Down