1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-10 18:11:04 +09:00

Move portable RID graph into runtime and clean-up (#92211)

* Move portable RID graph into runtime and clean-up

1. Move portable RID graph into runtime
2. Allow updates to both the non-portable and portable RID graphs under
   source build.
3. Clean-up project and remove hacks

* Update README and delete test

* Fix RID graph update when the key already exists

* Update src/libraries/Microsoft.NETCore.Platforms/readme.md

Co-authored-by: Jan Kotas <jkotas@microsoft.com>

* Update src/libraries/Microsoft.NETCore.Platforms/readme.md

Co-authored-by: Andy Gocke <angocke@microsoft.com>

---------

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
Co-authored-by: Andy Gocke <angocke@microsoft.com>
This commit is contained in:
Viktor Hofer 2023-09-19 23:29:29 +02:00 committed by GitHub
parent 1185d19592
commit 3b9b4fd5fc
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 580 additions and 14555 deletions

View file

@ -471,4 +471,11 @@
<CustomBeforeNoTargets>$(RepositoryEngineeringDir)NoTargetsSdk.BeforeTargets.targets</CustomBeforeNoTargets>
<CustomAfterTraversalTargets>$(RepositoryEngineeringDir)TraversalSdk.AfterTargets.targets</CustomAfterTraversalTargets>
</PropertyGroup>
<PropertyGroup>
<!-- Keep in sync with outputs defined in Microsoft.NETCore.Platforms.csproj. -->
<BundledRuntimeIdentifierGraphFile>$([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'Microsoft.NETCore.Platforms', 'runtime.json'))</BundledRuntimeIdentifierGraphFile>
<BundledRuntimeIdentifierGraphFile Condition="!Exists('$(BundledRuntimeIdentifierGraphFile)')">$([MSBuild]::NormalizePath('$(LibrariesProjectRoot)', 'Microsoft.NETCore.Platforms', 'src', 'runtime.json'))</BundledRuntimeIdentifierGraphFile>
</PropertyGroup>
</Project>

View file

@ -217,4 +217,5 @@
<InstallersRelativePath>Runtime/$(SharedFrameworkNugetVersion)/</InstallersRelativePath>
</PropertyGroup>
</Target>
</Project>

View file

@ -1,14 +0,0 @@
<Project>
<!-- Set PackageId to a temporary value to avoid NuGet restore cycle error NU1108: https://github.com/NuGet/Home/issues/6754 -->
<PropertyGroup>
<_PackageIdTemp>$(PackageId)</_PackageIdTemp>
<PackageId>$(PackageId)_temp</PackageId>
</PropertyGroup>
<Target Name="_UpdatePackageId"
BeforeTargets="$(PackDependsOn)" >
<PropertyGroup>
<PackageId>$(_PackageIdTemp)</PackageId>
</PropertyGroup>
</Target>
</Project>

View file

@ -127,7 +127,6 @@
<Import Project="$(RepositoryEngineeringDir)testing\coverage.targets" Condition="'$(EnableRunSettingsSupport)' == 'true' or '$(EnableCoverageSupport)' == 'true'" />
<Import Project="$(RepositoryEngineeringDir)illink.targets" Condition="'$(IsSourceProject)' == 'true' or '$(ExplicitlyImportCustomILLinkTargets)' == 'true'" />
<Import Project="$(RepositoryEngineeringDir)AvoidRestoreCycleOnSelfReference.targets" Condition="'$(AvoidRestoreCycleOnSelfReference)' == 'true'" />
<Import Project="$(RepositoryEngineeringDir)nativeSanitizers.targets" />
<ItemGroup Condition="'$(UseTargetFrameworkPackage)' != 'false'">

View file

@ -1,102 +1,24 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{39BCA125-321F-490F-AD4E-28DCB4406969}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NETCore.Platforms", "src\Microsoft.NETCore.Platforms.csproj", "{BFFF96CC-06AA-4291-9F93-3E77F23DBB11}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NETCore.Platforms.Tests", "tests\Microsoft.NETCore.Platforms.Tests.csproj", "{0C60F372-5C73-4BFA-9B91-5659C88F9750}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibraryImportGenerator", "..\System.Runtime.InteropServices\gen\LibraryImportGenerator\LibraryImportGenerator.csproj", "{45449066-3A31-43E5-B705-20D667080A23}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Interop.SourceGeneration", "..\System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj", "{2E3568B1-EC27-4F02-BC0E-71DD3FD7735B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILLink.CodeFixProvider", "..\..\tools\illink\src\ILLink.CodeFix\ILLink.CodeFixProvider.csproj", "{BF52D0B1-1AD8-49DB-B910-9F9D7D41292A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILLink.RoslynAnalyzer", "..\..\tools\illink\src\ILLink.RoslynAnalyzer\ILLink.RoslynAnalyzer.csproj", "{BEFED0BD-AD56-4FAC-B9A7-CCE8BB5F8768}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILLink.Tasks", "..\..\tools\illink\src\ILLink.Tasks\ILLink.Tasks.csproj", "{41387647-9500-448C-B021-B4837947EF62}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Linker", "..\..\tools\illink\src\linker\Mono.Linker.csproj", "{281E9ED5-5F16-475D-B50B-501633FCC8C6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Linker", "..\..\tools\illink\src\linker\ref\Mono.Linker.csproj", "{20346A2D-06DD-4E3E-96D4-C9BF32697AE3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{E4827496-6F39-4CA0-8F4A-ACDE9DFEBE5C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F96FBD24-3BB3-4D02-9884-4D90F94DD3C0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{E8010E1D-FDAF-481D-AA34-3B115B667E4B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "tools\gen", "{2B7F6369-F9CA-493A-A105-B7B7B697A31F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "tools\src", "{A527D4DF-4D57-40AE-B63B-1945F1D4FF3F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "tools\ref", "{EB2B9277-DB89-4554-8499-C3240163E875}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{0F4ABB64-2357-49FB-9BD3-8FEFC2A650E6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{39BCA125-321F-490F-AD4E-28DCB4406969}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{39BCA125-321F-490F-AD4E-28DCB4406969}.Debug|Any CPU.Build.0 = Debug|Any CPU
{39BCA125-321F-490F-AD4E-28DCB4406969}.Release|Any CPU.ActiveCfg = Release|Any CPU
{39BCA125-321F-490F-AD4E-28DCB4406969}.Release|Any CPU.Build.0 = Release|Any CPU
{BFFF96CC-06AA-4291-9F93-3E77F23DBB11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BFFF96CC-06AA-4291-9F93-3E77F23DBB11}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BFFF96CC-06AA-4291-9F93-3E77F23DBB11}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BFFF96CC-06AA-4291-9F93-3E77F23DBB11}.Release|Any CPU.Build.0 = Release|Any CPU
{0C60F372-5C73-4BFA-9B91-5659C88F9750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0C60F372-5C73-4BFA-9B91-5659C88F9750}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0C60F372-5C73-4BFA-9B91-5659C88F9750}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0C60F372-5C73-4BFA-9B91-5659C88F9750}.Release|Any CPU.Build.0 = Release|Any CPU
{45449066-3A31-43E5-B705-20D667080A23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{45449066-3A31-43E5-B705-20D667080A23}.Debug|Any CPU.Build.0 = Debug|Any CPU
{45449066-3A31-43E5-B705-20D667080A23}.Release|Any CPU.ActiveCfg = Release|Any CPU
{45449066-3A31-43E5-B705-20D667080A23}.Release|Any CPU.Build.0 = Release|Any CPU
{2E3568B1-EC27-4F02-BC0E-71DD3FD7735B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E3568B1-EC27-4F02-BC0E-71DD3FD7735B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E3568B1-EC27-4F02-BC0E-71DD3FD7735B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2E3568B1-EC27-4F02-BC0E-71DD3FD7735B}.Release|Any CPU.Build.0 = Release|Any CPU
{BF52D0B1-1AD8-49DB-B910-9F9D7D41292A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BF52D0B1-1AD8-49DB-B910-9F9D7D41292A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BF52D0B1-1AD8-49DB-B910-9F9D7D41292A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BF52D0B1-1AD8-49DB-B910-9F9D7D41292A}.Release|Any CPU.Build.0 = Release|Any CPU
{BEFED0BD-AD56-4FAC-B9A7-CCE8BB5F8768}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BEFED0BD-AD56-4FAC-B9A7-CCE8BB5F8768}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BEFED0BD-AD56-4FAC-B9A7-CCE8BB5F8768}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BEFED0BD-AD56-4FAC-B9A7-CCE8BB5F8768}.Release|Any CPU.Build.0 = Release|Any CPU
{41387647-9500-448C-B021-B4837947EF62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{41387647-9500-448C-B021-B4837947EF62}.Debug|Any CPU.Build.0 = Debug|Any CPU
{41387647-9500-448C-B021-B4837947EF62}.Release|Any CPU.ActiveCfg = Release|Any CPU
{41387647-9500-448C-B021-B4837947EF62}.Release|Any CPU.Build.0 = Release|Any CPU
{281E9ED5-5F16-475D-B50B-501633FCC8C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{281E9ED5-5F16-475D-B50B-501633FCC8C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{281E9ED5-5F16-475D-B50B-501633FCC8C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{281E9ED5-5F16-475D-B50B-501633FCC8C6}.Release|Any CPU.Build.0 = Release|Any CPU
{20346A2D-06DD-4E3E-96D4-C9BF32697AE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{20346A2D-06DD-4E3E-96D4-C9BF32697AE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{20346A2D-06DD-4E3E-96D4-C9BF32697AE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20346A2D-06DD-4E3E-96D4-C9BF32697AE3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{39BCA125-321F-490F-AD4E-28DCB4406969} = {E4827496-6F39-4CA0-8F4A-ACDE9DFEBE5C}
{0C60F372-5C73-4BFA-9B91-5659C88F9750} = {E4827496-6F39-4CA0-8F4A-ACDE9DFEBE5C}
{BFFF96CC-06AA-4291-9F93-3E77F23DBB11} = {F96FBD24-3BB3-4D02-9884-4D90F94DD3C0}
{45449066-3A31-43E5-B705-20D667080A23} = {E8010E1D-FDAF-481D-AA34-3B115B667E4B}
{2E3568B1-EC27-4F02-BC0E-71DD3FD7735B} = {E8010E1D-FDAF-481D-AA34-3B115B667E4B}
{BF52D0B1-1AD8-49DB-B910-9F9D7D41292A} = {2B7F6369-F9CA-493A-A105-B7B7B697A31F}
{BEFED0BD-AD56-4FAC-B9A7-CCE8BB5F8768} = {2B7F6369-F9CA-493A-A105-B7B7B697A31F}
{2B7F6369-F9CA-493A-A105-B7B7B697A31F} = {0F4ABB64-2357-49FB-9BD3-8FEFC2A650E6}
{41387647-9500-448C-B021-B4837947EF62} = {A527D4DF-4D57-40AE-B63B-1945F1D4FF3F}
{281E9ED5-5F16-475D-B50B-501633FCC8C6} = {A527D4DF-4D57-40AE-B63B-1945F1D4FF3F}
{A527D4DF-4D57-40AE-B63B-1945F1D4FF3F} = {0F4ABB64-2357-49FB-9BD3-8FEFC2A650E6}
{20346A2D-06DD-4E3E-96D4-C9BF32697AE3} = {EB2B9277-DB89-4554-8499-C3240163E875}
{EB2B9277-DB89-4554-8499-C3240163E875} = {0F4ABB64-2357-49FB-9BD3-8FEFC2A650E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E946A528-C3E7-48EC-AD6D-AE84ED2B11AC}

View file

@ -1,5 +1,5 @@
# Runtime IDs
The package `Microsoft.NETCore.Platforms` defines the runtime identifiers (RIDs) used by .NET packages to represent runtime-specific assets in NuGet packages.
The `Microsoft.NETCore.Platforms` transport package contains the portable and non-portable runtime identifier graph files for redistribution in the dotnet/sdk repository.
## What is a RID?
A RID is an opaque string that identifies a platform. RIDs have relationships to other RIDs by "importing" the other RID. In that way a RID is a directed graph of compatible RIDs.
@ -76,80 +76,4 @@ runtimes/win/lib/netstandard1.0/foo.dll
When resolving for netstandard1.5/win7-x64 will select `lib/netstandard1.5/foo.dll` for the compile asset and `runtimes/win/lib/netstandard1.0/foo.dll` for the runtime asset.
## Adding new RIDs
### Why do I need to add a new RID?
NuGet's extensibility mechanism for platform-specific assets requires a RID be defined for any platform that needs assets specific to that platform. Unlike TFMs, which have a known relationship in NuGet (eg net4.5 is compatible with net4.0), RIDs are opaque strings which NuGet knows nothing about. The definition and relationship of RIDs comes solely from the `runtime.json` files within the root of the packages referenced by the project.
As such, whenever we want to put a new RID in a project.json in order to get assets specific for that RID we have to define the rid in some package. Typically that package is `Microsoft.NETCore.Platforms` if the RID is "official". If you'd like to prototype you can put the RID in any other package and so long as that package is referenced you can use that RID.
### Do I really need to add a new RID?
If you're prototyping on a platform that is compatible with an existing platform then you can reuse the RID for that existing platform. New RIDs are only needed when an asset needs to be different on a particular platform.
`Microsoft.NETCore.Platforms` attempts to define all RIDs that packages may need, and as such will define RIDs for platforms that we don't actually cross compile for. This is to support higher-level packages, 3rd party packages, that may need to cross-compile for that RID.
### Adding a new OS
Add a new `RuntimeGroup` item in `runtimeGroups.props`.
For example:
```xml
<RuntimeGroup Include="myLinuxDistro">
<Parent>linux</Parent>
<Architectures>x86;x64;arm</Architectures>
<Versions>42.0;43.0</Versions>
</RuntimeGroup>
```
This will create a new RID for `myLinuxDistro` where `myLinuxDistro` should be the string used for the `ID=` value in the `/etc/os-release` file.
Whenever modifying the `runtimeGroups.props` make sure to pack the project via the `dotnet pack` command and inspect if the generated package contains the desired changes.
RuntimeGroup items have the following format:
- `Identity`: the base string for the RID, without version architecture, or qualifiers.
- `Parent`: the base string for the parent of this RID. This RID will be imported by the baseRID, architecture-specific, and qualifier-specific RIDs (with the latter two appending appropriate architecture and qualifiers).
- `Versions`: A list of strings delimited by semi-colons that represent the versions for this RID.
- `TreatVersionsAsCompatible`: Default is true. When true, version-specific RIDs will import the previous version-specific RID in the Versions list, with the first version importing the version-less RID. When false all version-specific RIDs will import the version-less RID (bypassing previous version-specific RIDs)
- `OmitVersionDelimiter`: Default is false. When true no characters will separate the base RID and version (EG: win7). When false a '.' will separate the base RID and version (EG: osx.10.12).
- `ApplyVersionsToParent`: Default is false. When true, version-specific RIDs will import version-specific Parent RIDs similar to is done for architecture and qualifier (see Parent above).
- `Architectures`: A list of strings delimited by semi-colons that represent the architectures for this RID.
- `AdditionalQualifiers`: A list of strings delimited by semi-colons that represent the additional qualifiers for this RID. Additional qualifers do not stack, each only applies to the qualifier-less RIDs (so as not to cause combinatorial exponential growth of RIDs).
### Adding a new version to an existing OS
Find the existing `RuntimeGroup` in `runtimeGroups.props` and add the version to the list of `Versions`, separated by a semi-colon.
If the version you are adding needs to be treated as not-compatible with previous versions and the `RuntimeGroup` has not set `TreatVersionsAsCompatible`=`false` then you may create a new `RuntimeGroup` to represent the new compatibility band.
### Checking your work
After making a change to `runtimeGroups.props` you can examine the resulting changes in `runtime.json` and `runtime.compatibility.json`.
`runtime.json` is the graph representation of the RIDs and is what ships in the package.
`runtime.compatibility.json` is a flattened version of the graph that shows the RID precedence for each RID in the graph.
### Version compatibility
Version compatibility is represented through imports. If a platform is considered compatible with another version of the same platform, or a specific version of another platform, then it can import that platform. This permits packages to reuse assets that were built for the imported platform on the compatible platform. Compatibility here is a bit nebulous because inevitably different platforms will have observable differences that can cause compatibility problems. For the purposes of RIDs we'll try to represent compatibility as versions of a platform that are explicitly advertised as being compatible with a previous version and/or another platform and don't have any known broad breaking changes. It is usually better to opt to treat platforms as compatible since that enables the scenario of building an asset for a particular version and using that in future versions, otherwise you force people to cross-compile for all future versions the moment they target a specific version.
## Appendix : details of RID graph generation
### Naming convention
We use the following convention in all newly-defined RIDs. Some RIDs (win7-x64, win8-x64) predate this convention and don't follow it, but all new RIDs should follow it.
`[os name].[version]-[architecture]-[additional qualifiers]`, for example `osx.10.10-x64` or `ubuntu.14.04-x64`
- `[os name]` can contain any characters other than `.`
- `[version]` can contain any characters other than `-`. Typically a numeric version like 14.04 or 10.0.
- `[architecture]` can contain any characters other than `-`. Typically: `x86`, `x64`, `arm`, `arm64`
- `[additional qualifiers]` can be things like `aot`. Used to further differentiate different platforms.
For all of these we strive to make them something that can be uniquely discoverable at runtime, so that a RID may be computed from an executing application. As such these properties should be derivable from `/etc/os-release` or similar platform APIs / data.
### Import convention
Imports should be used when the added RID is considered compatible with an existing RID.
1. Architecture-specific RIDs should first import the architecture-less RID. EG: `osx.10.11-x64` should first import `osx.10.11`.
2. Architecture-specific RIDs that are compatible with a previous version RID for the same OS should then import the previous version, architecture specific RID. EG: `osx.10.11-x64` should then import `osx.10.10-x64`. If there is no earlier compatible/supported version, then a versionless RID should be imported. EG: `osx.10.10-x64` should import `osx-x64`.
3. Architecture-less RIDs that are compatible with a previous version RID for the same OS should then import the previous version, architecture neutral RID. EG: `osx.10.11` should import `osx.10.10`. If there is no earlier compatible/supported version, then a versionless RID should be imported. EG: `osx.10.10` should import `osx`.
4. Version-less RIDs should import an OS category. EG: `osx-x64` should import `unix-x64`, `osx` should import `unix`.
### Advanced RuntimeGroup metadata
The following options can be used under special circumstances but break the normal precedence rules we try to establish by generating the RID graph from common logic. These options make it possible to create a RID fallback chain that doesn't match the rest of the RIDs and therefore is hard for developers/package authors to reason about. Only use these options for cases where you know what you are doing and have carefully reviewed the resulting RID fallbacks using the CompatibliltyMap.
- `OmitRIDs`: A list of strings delimited by semi-colons that represent RIDs calculated from this RuntimeGroup that should be omitted from the RuntimeGraph. These RIDs will not be referenced nor defined.
- `OmitRIDDefinitions`: A list of strings delimited by semi-colons that represent RIDs calculated from this RuntimeGroup that should be omitted from the RuntimeGraph. These RIDs will not be defined by this RuntimeGroup, but will be referenced: useful in case some other RuntimeGroup (or runtime.json template) defines them.
- `OmitRIDReferences`: A list of strings delimited by semi-colons that represent RIDs calculated from this RuntimeGroup that should be omitted from the RuntimeGraph. These RIDs will be defined but not referenced by this RuntimeGroup.
The RID graphs should be only updated with new base OSes, architectures, or C standard libraries. The RID graphs shouldn't be updated with new OS flavor- and version-specific RIDs anymore. Build from source automatically adds the non-portable distro RID encoded via the `OutputRID` property into the RID graph which allows build tools to target that RID.

View file

@ -1,111 +0,0 @@
// 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.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
namespace Microsoft.NETCore.Platforms.BuildTasks
{
/// <summary>
/// Used to enable app-local assembly unification.
/// </summary>
internal static class AssemblyResolver
{
static AssemblyResolver()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
/// <summary>
/// Call to enable the assembly resolver for the current AppDomain.
/// </summary>
public static void Enable()
{
// intentionally empty. This is just meant to ensure the static constructor
// has run.
}
[UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file",
Justification = "The code has a fallback to use AppDomain.CurrentDomain.BaseDirectory so it will work correctly in single-file")]
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
// apply any existing policy
AssemblyName referenceName = new AssemblyName(AppDomain.CurrentDomain.ApplyPolicy(args.Name));
string fileName = referenceName.Name + ".dll";
string assemblyPath;
string probingPath;
Assembly assm;
// look next to requesting assembly
assemblyPath = args.RequestingAssembly?.Location;
if (!string.IsNullOrEmpty(assemblyPath))
{
probingPath = Path.Combine(Path.GetDirectoryName(assemblyPath), fileName);
Debug.WriteLine($"Considering {probingPath} based on RequestingAssembly");
if (Probe(probingPath, referenceName.Version, out assm))
{
return assm;
}
}
// look next to the executing assembly
assemblyPath = Assembly.GetExecutingAssembly().Location;
if (!string.IsNullOrEmpty(assemblyPath))
{
probingPath = Path.Combine(Path.GetDirectoryName(assemblyPath), fileName);
Debug.WriteLine($"Considering {probingPath} based on ExecutingAssembly");
if (Probe(probingPath, referenceName.Version, out assm))
{
return assm;
}
}
// look in AppDomain base directory
probingPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
Debug.WriteLine($"Considering {probingPath} based on BaseDirectory");
if (Probe(probingPath, referenceName.Version, out assm))
{
return assm;
}
// look in current directory
Debug.WriteLine($"Considering {fileName}");
if (Probe(fileName, referenceName.Version, out assm))
{
return assm;
}
return null;
}
/// <summary>
/// Considers a path to load for satisfying an assembly ref and loads it
/// if the file exists and version is sufficient.
/// </summary>
/// <param name="filePath">Path to consider for load</param>
/// <param name="minimumVersion">Minimum version to consider</param>
/// <param name="assembly">loaded assembly</param>
/// <returns>true if assembly was loaded</returns>
private static bool Probe(string filePath, Version minimumVersion, out Assembly assembly)
{
if (File.Exists(filePath))
{
AssemblyName name = AssemblyName.GetAssemblyName(filePath);
if (name.Version >= minimumVersion)
{
assembly = Assembly.Load(name);
return true;
}
}
assembly = null;
return false;
}
}
}

View file

@ -1,13 +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.NETCore.Platforms.BuildTasks
{
public partial class BuildTask
{
static BuildTask()
{
AssemblyResolver.Enable();
}
}
}

View file

@ -1,151 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System;
namespace Microsoft.NETCore.Platforms.BuildTasks
{
public abstract partial class BuildTask : ITask
{
private Log _log;
internal Log Log
{
get { return _log ??= new Log(new TaskLoggingHelper(this)); }
}
public BuildTask()
{
}
public IBuildEngine BuildEngine
{
get;
set;
}
public ITaskHost HostObject
{
get;
set;
}
public abstract bool Execute();
}
internal sealed class Log : ILog
{
private readonly TaskLoggingHelper _logger;
public Log(TaskLoggingHelper logger)
{
_logger = logger;
}
public void LogError(string message, params object[] messageArgs)
{
_logger.LogError(message, messageArgs);
}
public void LogErrorFromException(Exception exception, bool showStackTrace)
{
_logger.LogErrorFromException(exception, showStackTrace);
}
public void LogMessage(string message, params object[] messageArgs)
{
_logger.LogMessage(message, messageArgs);
}
public void LogMessage(LogImportance importance, string message, params object[] messageArgs)
{
_logger.LogMessage((MessageImportance)importance, message, messageArgs);
}
public void LogWarning(string message, params object[] messageArgs)
{
_logger.LogWarning(message, messageArgs);
}
public bool HasLoggedErrors { get { return _logger.HasLoggedErrors; } }
}
public enum LogImportance
{
Low = MessageImportance.Low,
Normal = MessageImportance.Normal,
High = MessageImportance.High
}
public interface ILog
{
//
// Summary:
// Logs an error with the specified message.
//
// Parameters:
// message:
// The message.
//
// messageArgs:
// Optional arguments for formatting the message string.
//
// Exceptions:
// T:System.ArgumentNullException:
// message is null.
void LogError(string message, params object[] messageArgs);
//
// Summary:
// Logs a message with the specified string.
//
// Parameters:
// message:
// The message.
//
// messageArgs:
// The arguments for formatting the message.
//
// Exceptions:
// T:System.ArgumentNullException:
// message is null.
void LogMessage(string message, params object[] messageArgs);
//
// Summary:
// Logs a message with the specified string and importance.
//
// Parameters:
// importance:
// One of the enumeration values that specifies the importance of the message.
//
// message:
// The message.
//
// messageArgs:
// The arguments for formatting the message.
//
// Exceptions:
// T:System.ArgumentNullException:
// message is null.
void LogMessage(LogImportance importance, string message, params object[] messageArgs);
//
// Summary:
// Logs a warning with the specified message.
//
// Parameters:
// message:
// The message.
//
// messageArgs:
// Optional arguments for formatting the message string.
//
// Exceptions:
// T:System.ArgumentNullException:
// message is null.
void LogWarning(string message, params object[] messageArgs);
}
}

View file

@ -0,0 +1,9 @@
<Project>
<PropertyGroup>
<IsSourceProject>false</IsSourceProject>
</PropertyGroup>
<Import Project="..\..\Directory.Build.props" />
</Project>

View file

@ -1,46 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Linq;
using Microsoft.Build.Framework;
namespace Microsoft.NETCore.Platforms.BuildTasks
{
public static class Extensions
{
public static string GetString(this ITaskItem taskItem, string metadataName)
{
var metadataValue = taskItem.GetMetadata(metadataName)?.Trim();
return string.IsNullOrEmpty(metadataValue) ? null : metadataValue;
}
public static bool GetBoolean(this ITaskItem taskItem, string metadataName, bool defaultValue = false)
{
bool result;
var metadataValue = taskItem.GetMetadata(metadataName);
if (!bool.TryParse(metadataValue, out result))
{
result = defaultValue;
}
return result;
}
public static IEnumerable<string> GetStrings(this ITaskItem taskItem, string metadataName)
{
var metadataValue = taskItem.GetMetadata(metadataName)?.Trim();
if (!string.IsNullOrEmpty(metadataValue))
{
return metadataValue.Split(';').Where(v => !string.IsNullOrEmpty(v.Trim())).ToArray();
}
return Enumerable.Empty<string>();
}
public static IEnumerable<T> NullAsEmpty<T>(this IEnumerable<T> source)
{
return source ?? Enumerable.Empty<T>();
}
}
}

View file

@ -1,427 +0,0 @@
// 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.Xml.Linq;
using Microsoft.Build.Framework;
using Newtonsoft.Json;
using NuGet.RuntimeModel;
namespace Microsoft.NETCore.Platforms.BuildTasks
{
public class GenerateRuntimeGraph : BuildTask
{
/// <summary>
/// A set of RuntimeGroups that can be used to generate a runtime graph
/// Identity: the base string for the RID, without version architecture, or qualifiers.
/// Parent: the base string for the parent of this RID. This RID will be imported by the baseRID, architecture-specific,
/// and qualifier-specific RIDs (with the latter two appending appropriate architecture and qualifiers).
/// Versions: A list of strings delimited by semi-colons that represent the versions for this RID.
/// TreatVersionsAsCompatible: Default is true. When true, version-specific RIDs will import the previous
/// version-specific RID in the Versions list, with the first version importing the version-less RID.
/// When false all version-specific RIDs will import the version-less RID (bypassing previous version-specific RIDs)
/// OmitVersionDelimiter: Default is false. When true no characters will separate the base RID and version (EG: win7).
/// When false a '.' will separate the base RID and version (EG: osx.10.12).
/// ApplyVersionsToParent: Default is false. When true, version-specific RIDs will import version-specific Parent RIDs
/// similar to is done for architecture and qualifier (see Parent above).
/// Architectures: A list of strings delimited by semi-colons that represent the architectures for this RID.
/// AdditionalQualifiers: A list of strings delimited by semi-colons that represent the additional qualifiers for this RID.
/// Additional qualifers do not stack, each only applies to the qualifier-less RIDs (so as not to cause combinatorial
/// exponential growth of RIDs).
///
/// The following options can be used under special circumstances but break the normal precedence rules we try to establish
/// by generating the RID graph from common logic. These options make it possible to create a RID fallback chain that doesn't
/// match the rest of the RIDs and therefore is hard for developers/package authors to reason about.
/// Only use these options for cases where you know what you are doing and have carefully reviewed the resulting RID fallbacks
/// using the CompatibliltyMap.
/// OmitRIDs: A list of strings delimited by semi-colons that represent RIDs calculated from this RuntimeGroup that should
/// be omitted from the RuntimeGraph. These RIDs will not be referenced nor defined.
/// OmitRIDDefinitions: A list of strings delimited by semi-colons that represent RIDs calculated from this RuntimeGroup
/// that should be omitted from the RuntimeGraph. These RIDs will not be defined by this RuntimeGroup, but will be
/// referenced: useful in case some other RuntimeGroup (or runtime.json template) defines them.
/// OmitRIDReferences: A list of strings delimited by semi-colons that represent RIDs calculated from this RuntimeGroup
/// that should be omitted from the RuntimeGraph. These RIDs will be defined but not referenced by this RuntimeGroup.
/// </summary>
public ITaskItem[] RuntimeGroups
{
get;
set;
}
/// <summary>
/// Additional runtime identifiers to add to the graph.
/// </summary>
public string[] AdditionalRuntimeIdentifiers
{
get;
set;
}
/// <summary>
/// Parent RID to use for any unknown AdditionalRuntimeIdentifier.
/// </summary>
public string AdditionalRuntimeIdentifierParent
{
get;
set;
}
/// <summary>
/// Optional source Runtime.json to use as a starting point when merging additional RuntimeGroups
/// </summary>
public string SourceRuntimeJson
{
get;
set;
}
/// <summary>
/// Where to write the final runtime.json
/// </summary>
public string RuntimeJson
{
get;
set;
}
/// <summary>
/// Optionally, other runtime.jsons which may contain imported RIDs
/// </summary>
public string[] ExternalRuntimeJsons
{
get;
set;
}
/// <summary>
/// When defined, specifies the file to write compatibility precedence for each RID in the graph.
/// </summary>
public string CompatibilityMap
{
get;
set;
}
/// <summary>
/// True to write the generated runtime.json to RuntimeJson and compatibility map to CompatibilityMap, otherwise files are read and diffed
/// with generated versions and an error is emitted if they differ.
/// Setting UpdateRuntimeFiles will overwrite files even when the file is marked ReadOnly.
/// </summary>
public bool UpdateRuntimeFiles
{
get;
set;
}
/// <summary>
/// When defined, specifies the file to write a DGML representation of the runtime graph.
/// </summary>
public string RuntimeDirectedGraph
{
get;
set;
}
public override bool Execute()
{
if (RuntimeGroups != null && RuntimeGroups.Length != 0 && RuntimeJson == null)
{
Log.LogError($"{nameof(RuntimeJson)} argument must be specified when {nameof(RuntimeGroups)} is specified.");
return false;
}
RuntimeGraph runtimeGraph;
if (!string.IsNullOrEmpty(SourceRuntimeJson))
{
if (!File.Exists(SourceRuntimeJson))
{
Log.LogError($"{nameof(SourceRuntimeJson)} did not exist at {SourceRuntimeJson}.");
return false;
}
runtimeGraph = JsonRuntimeFormat.ReadRuntimeGraph(SourceRuntimeJson);
}
else
{
runtimeGraph = new RuntimeGraph();
}
List<RuntimeGroup> runtimeGroups = RuntimeGroups.NullAsEmpty().Select(i => new RuntimeGroup(i)).ToList();
AddRuntimeIdentifiers(runtimeGroups);
foreach (var runtimeGroup in runtimeGroups)
{
runtimeGraph = SafeMerge(runtimeGraph, runtimeGroup);
}
Dictionary<string, string> externalRids = new Dictionary<string, string>();
if (ExternalRuntimeJsons != null)
{
foreach (var externalRuntimeJson in ExternalRuntimeJsons)
{
RuntimeGraph externalRuntimeGraph = JsonRuntimeFormat.ReadRuntimeGraph(externalRuntimeJson);
foreach (var runtime in externalRuntimeGraph.Runtimes.Keys)
{
// don't check for duplicates, we merely care what is external
externalRids.Add(runtime, externalRuntimeJson);
}
}
}
ValidateImports(runtimeGraph, externalRids);
if (!string.IsNullOrEmpty(RuntimeJson))
{
if (UpdateRuntimeFiles)
{
EnsureWritable(RuntimeJson);
WriteRuntimeGraph(RuntimeJson, runtimeGraph);
}
else
{
// validate that existing file matches generated file
if (!File.Exists(RuntimeJson))
{
Log.LogError($"{nameof(RuntimeJson)} did not exist at {RuntimeJson} and {nameof(UpdateRuntimeFiles)} was not specified.");
}
else
{
var existingRuntimeGraph = JsonRuntimeFormat.ReadRuntimeGraph(RuntimeJson);
if (!existingRuntimeGraph.Equals(runtimeGraph))
{
Log.LogError($"The generated {nameof(RuntimeJson)} differs from {RuntimeJson} and {nameof(UpdateRuntimeFiles)} was not specified. Please specify {nameof(UpdateRuntimeFiles)}=true to commit the changes.");
}
}
}
}
if (!string.IsNullOrEmpty(CompatibilityMap))
{
var compatibilityMap = GetCompatibilityMap(runtimeGraph);
if (UpdateRuntimeFiles)
{
EnsureWritable(CompatibilityMap);
WriteCompatibilityMap(compatibilityMap, CompatibilityMap);
}
else
{
// validate that existing file matches generated file
if (!File.Exists(CompatibilityMap))
{
Log.LogError($"{nameof(CompatibilityMap)} did not exist at {CompatibilityMap} and {nameof(UpdateRuntimeFiles)} was not specified.");
}
else
{
var existingCompatibilityMap = ReadCompatibilityMap(CompatibilityMap);
if (!CompatibilityMapEquals(existingCompatibilityMap, compatibilityMap))
{
Log.LogError($"The generated {nameof(CompatibilityMap)} differs from {CompatibilityMap} and {nameof(UpdateRuntimeFiles)} was not specified. Please specify {nameof(UpdateRuntimeFiles)}=true to commit the changes.");
}
}
}
}
if (!string.IsNullOrEmpty(RuntimeDirectedGraph))
{
WriteRuntimeGraph(runtimeGraph, RuntimeDirectedGraph);
}
return !Log.HasLoggedErrors;
}
private static void EnsureWritable(string file)
{
if (File.Exists(file))
{
var existingAttributes = File.GetAttributes(file);
if ((existingAttributes & FileAttributes.ReadOnly) != 0)
{
File.SetAttributes(file, existingAttributes &= ~FileAttributes.ReadOnly);
}
}
}
public static void WriteRuntimeGraph(string filePath, RuntimeGraph runtimeGraph)
{
using (var fileStream = new FileStream(filePath, FileMode.Create))
using (var textWriter = new StreamWriter(fileStream))
using (var jsonWriter = new JsonTextWriter(textWriter))
using (var writer = new JsonObjectWriter(jsonWriter))
{
jsonWriter.Formatting = Formatting.Indented;
// workaround https://github.com/NuGet/Home/issues/9532
writer.WriteObjectStart();
JsonRuntimeFormat.WriteRuntimeGraph(writer, runtimeGraph);
writer.WriteObjectEnd();
}
}
private RuntimeGraph SafeMerge(RuntimeGraph existingGraph, RuntimeGroup runtimeGroup)
{
var runtimeGraph = runtimeGroup.GetRuntimeGraph();
foreach (var existingRuntimeDescription in existingGraph.Runtimes.Values)
{
RuntimeDescription newRuntimeDescription;
if (runtimeGraph.Runtimes.TryGetValue(existingRuntimeDescription.RuntimeIdentifier, out newRuntimeDescription))
{
// overlapping RID, ensure that the imports match (same ordering and content)
if (!existingRuntimeDescription.InheritedRuntimes.SequenceEqual(newRuntimeDescription.InheritedRuntimes))
{
Log.LogError($"RuntimeGroup {runtimeGroup.BaseRID} defines RID {newRuntimeDescription.RuntimeIdentifier} with imports {string.Join(";", newRuntimeDescription.InheritedRuntimes)} which differ from existing imports {string.Join(";", existingRuntimeDescription.InheritedRuntimes)}. You may avoid this by specifying {nameof(RuntimeGroup.OmitRIDDefinitions)} metadata with {newRuntimeDescription.RuntimeIdentifier}.");
}
}
}
return RuntimeGraph.Merge(existingGraph, runtimeGraph);
}
private void ValidateImports(RuntimeGraph runtimeGraph, Dictionary<string, string> externalRIDs)
{
foreach (var runtimeDescription in runtimeGraph.Runtimes.Values)
{
string externalRuntimeJson;
if (externalRIDs.TryGetValue(runtimeDescription.RuntimeIdentifier, out externalRuntimeJson))
{
Log.LogError($"Runtime {runtimeDescription.RuntimeIdentifier} is defined in both this RuntimeGraph and {externalRuntimeJson}.");
}
foreach (var import in runtimeDescription.InheritedRuntimes)
{
if (!runtimeGraph.Runtimes.ContainsKey(import) && !externalRIDs.ContainsKey(import))
{
Log.LogError($"Runtime {runtimeDescription.RuntimeIdentifier} imports {import} which is not defined.");
}
}
}
}
private void AddRuntimeIdentifiers(ICollection<RuntimeGroup> runtimeGroups)
{
if (AdditionalRuntimeIdentifiers == null || AdditionalRuntimeIdentifiers.Length == 0)
{
return;
}
RuntimeGroupCollection runtimeGroupCollection = new RuntimeGroupCollection(runtimeGroups);
foreach (string additionalRuntimeIdentifier in AdditionalRuntimeIdentifiers)
{
runtimeGroupCollection.AddRuntimeIdentifier(additionalRuntimeIdentifier, AdditionalRuntimeIdentifierParent);
}
}
private static Dictionary<string, IEnumerable<string>> GetCompatibilityMap(RuntimeGraph graph)
{
Dictionary<string, IEnumerable<string>> compatibilityMap = new Dictionary<string, IEnumerable<string>>();
foreach (var rid in graph.Runtimes.Keys.OrderBy(rid => rid, StringComparer.Ordinal))
{
compatibilityMap.Add(rid, graph.ExpandRuntime(rid));
}
return compatibilityMap;
}
private static IDictionary<string, IEnumerable<string>> ReadCompatibilityMap(string mapFile)
{
var serializer = new JsonSerializer();
using (var file = File.OpenText(mapFile))
using (var jsonTextReader = new JsonTextReader(file) { MaxDepth = null })
{
return serializer.Deserialize<IDictionary<string, IEnumerable<string>>>(jsonTextReader);
}
}
private static void WriteCompatibilityMap(Dictionary<string, IEnumerable<string>> compatibilityMap, string mapFile)
{
var serializer = new JsonSerializer()
{
Formatting = Formatting.Indented,
StringEscapeHandling = StringEscapeHandling.EscapeNonAscii
};
string directory = Path.GetDirectoryName(mapFile);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
using (var file = File.CreateText(mapFile))
{
serializer.Serialize(file, compatibilityMap);
}
}
private static bool CompatibilityMapEquals(IDictionary<string, IEnumerable<string>> left, Dictionary<string, IEnumerable<string>> right)
{
if (left.Count != right.Count)
{
return false;
}
foreach (var leftPair in left)
{
IEnumerable<string> rightValue;
if (!right.TryGetValue(leftPair.Key, out rightValue))
{
return false;
}
if (!rightValue.SequenceEqual(leftPair.Value))
{
return false;
}
}
return true;
}
private static readonly XNamespace s_dgmlns = @"http://schemas.microsoft.com/vs/2009/dgml";
private static void WriteRuntimeGraph(RuntimeGraph graph, string dependencyGraphFilePath)
{
var doc = new XDocument(new XElement(s_dgmlns + "DirectedGraph"));
var nodesElement = new XElement(s_dgmlns + "Nodes");
var linksElement = new XElement(s_dgmlns + "Links");
doc.Root.Add(nodesElement);
doc.Root.Add(linksElement);
foreach (var runtimeDescription in graph.Runtimes.Values)
{
nodesElement.Add(new XElement(s_dgmlns + "Node",
new XAttribute("Id", runtimeDescription.RuntimeIdentifier)));
foreach (var import in runtimeDescription.InheritedRuntimes)
{
linksElement.Add(new XElement(s_dgmlns + "Link",
new XAttribute("Source", runtimeDescription.RuntimeIdentifier),
new XAttribute("Target", import)));
}
}
using (var file = File.Create(dependencyGraphFilePath))
{
doc.Save(file);
}
}
}
}

View file

@ -1,86 +1,63 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppToolCurrent);$(NetFrameworkToolCurrent)</TargetFrameworks>
<AssemblyName>Microsoft.NETCore.Platforms.BuildTasks</AssemblyName>
<IsPackable>true</IsPackable>
<IsShipping>false</IsShipping>
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
<PackageDescription>Provides runtime information required to resolve target framework, platform, and runtime specific implementations of .NET packages.</PackageDescription>
<NoWarn>$(NoWarn);NU5128</NoWarn> <!-- No Dependencies-->
<!-- Opt out of features that aren't necessary for a local build task. -->
<Nullable>disable</Nullable>
<EnableBinPlacing>false</EnableBinPlacing>
<!-- This project should not build against the live built .NETCoreApp targeting pack as it contributes to the build itself. -->
<UseLocalTargetingRuntimePack>false</UseLocalTargetingRuntimePack>
<IncludeBuildOutput>false</IncludeBuildOutput>
<IncludeSymbols>false</IncludeSymbols>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<IsPackable>true</IsPackable>
<IsShipping>false</IsShipping>
<PackageId>$(MSBuildProjectName)</PackageId>
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
<PackageDescription>Provides runtime information required to resolve target framework, platform, and runtime specific implementations of .NETCore packages.</PackageDescription>
<NoWarn>$(NoWarn);NU5128</NoWarn> <!-- No Dependencies-->
<AvoidRestoreCycleOnSelfReference>true</AvoidRestoreCycleOnSelfReference>
<!-- TODO: Remove with AvoidRestoreCycleOnSelfReference hack. -->
<PackageValidationBaselineName>$(MSBuildProjectName)</PackageValidationBaselineName>
<BeforePack Condition="'$(AdditionalRuntimeIdentifiers)' == ''">UpdateRuntimeJson;$(BeforePack)</BeforePack>
<_generateRuntimeGraphTargetFramework Condition="'$(MSBuildRuntimeType)' == 'core'">$(NetCoreAppToolCurrent)</_generateRuntimeGraphTargetFramework>
<_generateRuntimeGraphTargetFramework Condition="'$(MSBuildRuntimeType)' != 'core'">net472</_generateRuntimeGraphTargetFramework>
<_generateRuntimeGraphTargetFramework Condition="'$(MSBuildRuntimeType)' != 'core'">$(NetFrameworkToolCurrent)</_generateRuntimeGraphTargetFramework>
<_generateRuntimeGraphTask>$([MSBuild]::NormalizePath('$(BaseOutputPath)', $(Configuration), '$(_generateRuntimeGraphTargetFramework)', '$(AssemblyName).dll'))</_generateRuntimeGraphTask>
<!-- When building from source, ensure the RID we're building for is part of the RID graph -->
<AdditionalRuntimeIdentifiers Condition="'$(DotNetBuildFromSource)' == 'true'">$(AdditionalRuntimeIdentifiers);$(OutputRID)</AdditionalRuntimeIdentifiers>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">
<Compile Include="BuildTask.Desktop.cs" />
<Compile Include="AssemblyResolver.cs" />
<Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\UnconditionalSuppressMessageAttribute.cs" />
<ItemGroup>
<AdditionalRuntimeIdentifiers Include="$(AdditionalRuntimeIdentifiers)" Imports="$(AdditionalRuntimeIdentifierParent)" />
<!-- When building from source, ensure the RID we're building for is part of the RID graph. -->
<AdditionalRuntimeIdentifiers Include="$(OutputRID)" Imports="$(AdditionalRuntimeIdentifierParent)" Condition="'$(DotNetBuildFromSource)' == 'true'" />
</ItemGroup>
<ItemGroup>
<Compile Include="BuildTask.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="GenerateRuntimeGraph.cs" />
<Compile Include="RID.cs" />
<Compile Include="RuntimeGroupCollection.cs" />
<Compile Include="RuntimeGroup.cs" />
<Compile Include="RuntimeVersion.cs" />
<Compile Include="UpdateRuntimeIdentifierGraph.cs" />
</ItemGroup>
<ItemGroup>
<Content Condition="'$(AdditionalRuntimeIdentifiers)' == ''" Include="runtime.json" PackagePath="/" />
<Content Condition="'$(AdditionalRuntimeIdentifiers)' != ''" Include="$(BaseOutputPath)runtime.json" PackagePath="/" />
<Content Include="$(PlaceholderFile)" PackagePath="lib/netstandard1.0" />
<ItemGroup Condition="'@(AdditionalRuntimeIdentifiers)' == ''">
<Content Include="runtime.json" PackagePath="/" />
<Content Include="PortableRuntimeIdentifierGraph.json" PackagePath="/" />
</ItemGroup>
<ItemGroup Condition="'@(AdditionalRuntimeIdentifiers)' != ''">
<Content Include="$(BaseOutputPath)runtime.json" PackagePath="/" />
<Content Include="$(BaseOutputPath)PortableRuntimeIdentifierGraph.json" PackagePath="/" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="$(MicrosoftBuildTasksCoreVersion)" />
<PackageReference Include="NuGet.ProjectModel" Version="$(NugetProjectModelVersion)" />
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
</ItemGroup>
<Import Project="runtimeGroups.props" />
<UsingTask TaskName="UpdateRuntimeIdentifierGraph" AssemblyFile="$(_generateRuntimeGraphTask)"/>
<Target Name="UpdateRuntimeIdentifierGraph"
AfterTargets="Build"
Condition="'@(AdditionalRuntimeIdentifiers)' != ''">
<!-- non portable RID graph -->
<UpdateRuntimeIdentifierGraph InputFile="runtime.json"
OutputFile="$(BaseOutputPath)runtime.json"
AdditionalRuntimeIdentifiers="@(AdditionalRuntimeIdentifiers)" />
<UsingTask TaskName="GenerateRuntimeGraph" AssemblyFile="$(_generateRuntimeGraphTask)"/>
<Target Name="GenerateRuntimeJson" AfterTargets="Build" Condition="'$(AdditionalRuntimeIdentifiers)' != ''">
<MakeDir Directories="$(IntermediateOutputPath)" />
<GenerateRuntimeGraph RuntimeGroups="@(RuntimeGroupWithQualifiers)"
AdditionalRuntimeIdentifiers="$(AdditionalRuntimeIdentifiers)"
AdditionalRuntimeIdentifierParent="$(AdditionalRuntimeIdentifierParent)"
RuntimeJson="$(BaseOutputPath)runtime.json"
UpdateRuntimeFiles="True" />
</Target>
<Target Name="UpdateRuntimeJson">
<!-- Generates a Runtime graph using RuntimeGroups and diffs it with the graph described by runtime.json and runtime.compatibility.json
Specifying UpdateRuntimeFiles=true skips the diff and updates those files.
The graph can be visualized using the generated dmgl -->
<MakeDir Directories="$(OutputPath)" />
<GenerateRuntimeGraph RuntimeGroups="@(RuntimeGroupWithQualifiers)"
RuntimeJson="runtime.json"
CompatibilityMap="runtime.compatibility.json"
RuntimeDirectedGraph="$(OutputPath)runtime.json.dgml"
UpdateRuntimeFiles="$(UpdateRuntimeFiles)" />
<!-- portable RID graph -->
<UpdateRuntimeIdentifierGraph InputFile="PortableRuntimeIdentifierGraph.json"
OutputFile="$(BaseOutputPath)PortableRuntimeIdentifierGraph.json"
AdditionalRuntimeIdentifiers="@(AdditionalRuntimeIdentifiers)" />
</Target>
</Project>

View file

@ -0,0 +1,475 @@
{
"runtimes": {
"android": {
"#import": [
"linux-bionic"
]
},
"android-arm": {
"#import": [
"android",
"linux-bionic-arm"
]
},
"android-arm64": {
"#import": [
"android",
"linux-bionic-arm64"
]
},
"android-x64": {
"#import": [
"android",
"linux-bionic-x64"
]
},
"android-x86": {
"#import": [
"android",
"linux-bionic-x86"
]
},
"any": {
"#import": [
"base"
]
},
"base": {
"#import": []
},
"browser": {
"#import": [
"any"
]
},
"browser-wasm": {
"#import": [
"browser"
]
},
"freebsd": {
"#import": [
"unix"
]
},
"freebsd-arm64": {
"#import": [
"freebsd",
"unix-arm64"
]
},
"freebsd-x64": {
"#import": [
"freebsd",
"unix-x64"
]
},
"haiku": {
"#import": [
"unix"
]
},
"haiku-x64": {
"#import": [
"haiku",
"unix-x64"
]
},
"illumos": {
"#import": [
"unix"
]
},
"illumos-x64": {
"#import": [
"illumos",
"unix-x64"
]
},
"ios": {
"#import": [
"unix"
]
},
"ios-arm": {
"#import": [
"ios",
"unix-arm"
]
},
"ios-arm64": {
"#import": [
"ios",
"unix-arm64"
]
},
"ios-x64": {
"#import": [
"ios",
"unix-x64"
]
},
"ios-x86": {
"#import": [
"ios",
"unix-x86"
]
},
"iossimulator": {
"#import": [
"ios"
]
},
"iossimulator-arm64": {
"#import": [
"iossimulator",
"ios-arm64"
]
},
"iossimulator-x64": {
"#import": [
"iossimulator",
"ios-x64"
]
},
"iossimulator-x86": {
"#import": [
"iossimulator",
"ios-x86"
]
},
"linux": {
"#import": [
"unix"
]
},
"linux-arm": {
"#import": [
"linux",
"unix-arm"
]
},
"linux-arm64": {
"#import": [
"linux",
"unix-arm64"
]
},
"linux-armel": {
"#import": [
"linux",
"unix-armel"
]
},
"linux-armv6": {
"#import": [
"linux",
"unix-armv6"
]
},
"linux-bionic": {
"#import": [
"linux"
]
},
"linux-bionic-arm": {
"#import": [
"linux-bionic",
"linux-arm"
]
},
"linux-bionic-arm64": {
"#import": [
"linux-bionic",
"linux-arm64"
]
},
"linux-bionic-x64": {
"#import": [
"linux-bionic",
"linux-x64"
]
},
"linux-bionic-x86": {
"#import": [
"linux-bionic",
"linux-x86"
]
},
"linux-loongarch64": {
"#import": [
"linux",
"unix-loongarch64"
]
},
"linux-mips64": {
"#import": [
"linux",
"unix-mips64"
]
},
"linux-musl": {
"#import": [
"linux"
]
},
"linux-musl-arm": {
"#import": [
"linux-musl",
"linux-arm"
]
},
"linux-musl-arm64": {
"#import": [
"linux-musl",
"linux-arm64"
]
},
"linux-musl-armel": {
"#import": [
"linux-musl",
"linux-armel"
]
},
"linux-musl-armv6": {
"#import": [
"linux-musl",
"linux-armv6"
]
},
"linux-musl-ppc64le": {
"#import": [
"linux-musl",
"linux-ppc64le"
]
},
"linux-musl-riscv64": {
"#import": [
"linux-musl",
"linux-riscv64"
]
},
"linux-musl-s390x": {
"#import": [
"linux-musl",
"linux-s390x"
]
},
"linux-musl-x64": {
"#import": [
"linux-musl",
"linux-x64"
]
},
"linux-musl-x86": {
"#import": [
"linux-musl",
"linux-x86"
]
},
"linux-ppc64le": {
"#import": [
"linux",
"unix-ppc64le"
]
},
"linux-riscv64": {
"#import": [
"linux",
"unix-riscv64"
]
},
"linux-s390x": {
"#import": [
"linux",
"unix-s390x"
]
},
"linux-x64": {
"#import": [
"linux",
"unix-x64"
]
},
"linux-x86": {
"#import": [
"linux",
"unix-x86"
]
},
"maccatalyst": {
"#import": [
"ios"
]
},
"maccatalyst-arm64": {
"#import": [
"maccatalyst",
"ios-arm64"
]
},
"maccatalyst-x64": {
"#import": [
"maccatalyst",
"ios-x64"
]
},
"osx": {
"#import": [
"unix"
]
},
"osx-arm64": {
"#import": [
"osx",
"unix-arm64"
]
},
"osx-x64": {
"#import": [
"osx",
"unix-x64"
]
},
"solaris": {
"#import": [
"unix"
]
},
"solaris-x64": {
"#import": [
"solaris",
"unix-x64"
]
},
"tvos": {
"#import": [
"unix"
]
},
"tvos-arm64": {
"#import": [
"tvos",
"unix-arm64"
]
},
"tvos-x64": {
"#import": [
"tvos",
"unix-x64"
]
},
"tvossimulator": {
"#import": [
"tvos"
]
},
"tvossimulator-arm64": {
"#import": [
"tvossimulator",
"tvos-arm64"
]
},
"tvossimulator-x64": {
"#import": [
"tvossimulator",
"tvos-x64"
]
},
"unix": {
"#import": [
"any"
]
},
"unix-arm": {
"#import": [
"unix"
]
},
"unix-arm64": {
"#import": [
"unix"
]
},
"unix-armel": {
"#import": [
"unix"
]
},
"unix-armv6": {
"#import": [
"unix"
]
},
"unix-loongarch64": {
"#import": [
"unix"
]
},
"unix-mips64": {
"#import": [
"unix"
]
},
"unix-ppc64le": {
"#import": [
"unix"
]
},
"unix-riscv64": {
"#import": [
"unix"
]
},
"unix-s390x": {
"#import": [
"unix"
]
},
"unix-x64": {
"#import": [
"unix"
]
},
"unix-x86": {
"#import": [
"unix"
]
},
"wasi": {
"#import": [
"any"
]
},
"wasi-wasm": {
"#import": [
"wasi"
]
},
"win": {
"#import": [
"any"
]
},
"win-arm": {
"#import": [
"win"
]
},
"win-arm64": {
"#import": [
"win"
]
},
"win-x64": {
"#import": [
"win"
]
},
"win-x86": {
"#import": [
"win"
]
}
}
}

View file

@ -1,208 +0,0 @@
// 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.Diagnostics;
using System.Text;
namespace Microsoft.NETCore.Platforms.BuildTasks
{
public class RID
{
internal const char VersionDelimiter = '.';
internal const char ArchitectureDelimiter = '-';
internal const char QualifierDelimiter = '-';
public string BaseRID { get; set; }
public bool OmitVersionDelimiter { get; set; }
public RuntimeVersion Version { get; set; }
public string Architecture { get; set; }
public string Qualifier { get; set; }
public override string ToString()
{
StringBuilder builder = new StringBuilder(BaseRID);
if (HasVersion)
{
if (!OmitVersionDelimiter)
{
builder.Append(VersionDelimiter);
}
builder.Append(Version);
}
if (HasArchitecture)
{
builder.Append(ArchitectureDelimiter);
builder.Append(Architecture);
}
if (HasQualifier)
{
builder.Append(QualifierDelimiter);
builder.Append(Qualifier);
}
return builder.ToString();
}
private enum RIDPart : int
{
Base = 0,
Version,
Architecture,
Qualifier,
Max = Qualifier
}
public static RID Parse(string runtimeIdentifier, bool noQualifier)
{
string[] parts = new string[(int)RIDPart.Max + 1];
bool omitVersionDelimiter = true;
RIDPart parseState = RIDPart.Base;
int partStart = 0, partLength;
// qualifier is indistinguishable from arch so we cannot distinguish it for parsing purposes
Debug.Assert(ArchitectureDelimiter == QualifierDelimiter);
for (int i = 0; i < runtimeIdentifier.Length; i++)
{
char current = runtimeIdentifier[i];
partLength = i - partStart;
switch (parseState)
{
case RIDPart.Base:
// treat any number as the start of the version
if (current == VersionDelimiter || (current >= '0' && current <= '9'))
{
SetPart();
partStart = i;
if (current == VersionDelimiter)
{
omitVersionDelimiter = false;
partStart = i + 1;
}
parseState = RIDPart.Version;
}
// version might be omitted
else if (current == ArchitectureDelimiter)
{
// The qualifier delimiter and architecture delimiter are the same.
// When there is no qualifier, there will be one delimiter past the base part
// for the architecture.
// So if we see another delimiter later in the string (for the architecture),
// extend the base part instead of starting the architecture part.
if (noQualifier && runtimeIdentifier.IndexOf(ArchitectureDelimiter, i + 1) != -1)
{
break;
}
// ensure there's no version later in the string
if (runtimeIdentifier.IndexOf(VersionDelimiter, i) != -1)
{
break;
}
SetPart();
partStart = i + 1; // skip delimiter
parseState = RIDPart.Architecture;
}
break;
case RIDPart.Version:
if (current == ArchitectureDelimiter)
{
SetPart();
partStart = i + 1; // skip delimiter
parseState = RIDPart.Architecture;
}
break;
case RIDPart.Architecture:
if (current == QualifierDelimiter)
{
SetPart();
partStart = i + 1; // skip delimiter
parseState = RIDPart.Qualifier;
}
break;
default:
break;
}
}
partLength = runtimeIdentifier.Length - partStart;
if (partLength > 0)
{
SetPart();
}
string GetPart(RIDPart part)
{
return parts[(int)part];
}
void SetPart()
{
if (partLength == 0)
{
throw new ArgumentException($"Unexpected delimiter at position {partStart} in \"{runtimeIdentifier}\"");
}
parts[(int)parseState] = runtimeIdentifier.Substring(partStart, partLength);
}
string version = GetPart(RIDPart.Version);
if (version == null)
{
omitVersionDelimiter = false;
}
return new RID()
{
BaseRID = GetPart(RIDPart.Base),
OmitVersionDelimiter = omitVersionDelimiter,
Version = version == null ? null : new RuntimeVersion(version),
Architecture = GetPart(RIDPart.Architecture),
Qualifier = GetPart(RIDPart.Qualifier)
};
}
public bool HasVersion => Version != null;
public bool HasArchitecture => Architecture != null;
public bool HasQualifier => Qualifier != null;
public override bool Equals(object obj)
{
return Equals(obj as RID);
}
public bool Equals(RID obj)
{
return object.ReferenceEquals(obj, this) ||
(obj is not null &&
BaseRID == obj.BaseRID &&
(Version == null || OmitVersionDelimiter == obj.OmitVersionDelimiter) &&
Version == obj.Version &&
Architecture == obj.Architecture &&
Qualifier == obj.Qualifier);
}
public override int GetHashCode()
{
#if NETFRAMEWORK
return BaseRID.GetHashCode();
#else
HashCode hashCode = default;
hashCode.Add(BaseRID);
hashCode.Add(Version);
hashCode.Add(Architecture);
hashCode.Add(Qualifier);
return hashCode.ToHashCode();
#endif
}
}
}

View file

@ -1,298 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Build.Framework;
using NuGet.RuntimeModel;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.NETCore.Platforms.BuildTasks
{
public class RuntimeGroup
{
private const string rootRID = "any";
public RuntimeGroup(ITaskItem item)
{
BaseRID = item.ItemSpec;
Parent = item.GetString(nameof(Parent));
Versions = new HashSet<RuntimeVersion>(item.GetStrings(nameof(Versions)).Select(v => new RuntimeVersion(v)));
TreatVersionsAsCompatible = item.GetBoolean(nameof(TreatVersionsAsCompatible), true);
OmitVersionDelimiter = item.GetBoolean(nameof(OmitVersionDelimiter));
ApplyVersionsToParent = item.GetBoolean(nameof(ApplyVersionsToParent));
Architectures = new HashSet<string>(item.GetStrings(nameof(Architectures)));
AdditionalQualifiers = new HashSet<string>(item.GetStrings(nameof(AdditionalQualifiers)));
OmitRIDs = new HashSet<string>(item.GetStrings(nameof(OmitRIDs)));
OmitRIDDefinitions = new HashSet<string>(item.GetStrings(nameof(OmitRIDDefinitions)));
OmitRIDReferences = new HashSet<string>(item.GetStrings(nameof(OmitRIDReferences)));
}
public RuntimeGroup(string baseRID, string parent, bool treatVersionsAsCompatible = true, bool omitVersionDelimiter = false, bool applyVersionsToParent = false, IEnumerable<string> additionalQualifiers = null)
{
BaseRID = baseRID;
Parent = parent;
Versions = new HashSet<RuntimeVersion>();
TreatVersionsAsCompatible = treatVersionsAsCompatible;
OmitVersionDelimiter = omitVersionDelimiter;
ApplyVersionsToParent = applyVersionsToParent;
Architectures = new HashSet<string>();
AdditionalQualifiers = new HashSet<string>(additionalQualifiers.NullAsEmpty());
OmitRIDs = new HashSet<string>();
OmitRIDDefinitions = new HashSet<string>();
OmitRIDReferences = new HashSet<string>();
}
public string BaseRID { get; }
public string Parent { get; }
public ICollection<RuntimeVersion> Versions { get; }
public bool TreatVersionsAsCompatible { get; }
public bool OmitVersionDelimiter { get; }
public bool ApplyVersionsToParent { get; }
public ICollection<string> Architectures { get; }
public ICollection<string> AdditionalQualifiers { get; }
public ICollection<string> OmitRIDs { get; }
public ICollection<string> OmitRIDDefinitions { get; }
public ICollection<string> OmitRIDReferences { get; }
public void ApplyRid(RID rid)
{
if (!rid.BaseRID.Equals(BaseRID, StringComparison.Ordinal))
{
throw new ArgumentException($"Cannot apply {nameof(RID)} with {nameof(RID.BaseRID)} {rid.BaseRID} to {nameof(RuntimeGroup)} with {nameof(RuntimeGroup.BaseRID)} {BaseRID}.", nameof(rid));
}
if (rid.HasArchitecture)
{
Architectures.Add(rid.Architecture);
}
if (rid.HasVersion)
{
Versions.Add(rid.Version);
}
if (rid.HasQualifier)
{
AdditionalQualifiers.Add(rid.Qualifier);
}
}
internal sealed class RIDMapping
{
public RIDMapping(RID runtimeIdentifier)
{
RuntimeIdentifier = runtimeIdentifier;
Imports = Enumerable.Empty<RID>();
}
public RIDMapping(RID runtimeIdentifier, IEnumerable<RID> imports)
{
RuntimeIdentifier = runtimeIdentifier;
Imports = imports;
}
public RID RuntimeIdentifier { get; }
public IEnumerable<RID> Imports { get; }
}
internal RID CreateRuntime(string baseRid, RuntimeVersion version = null, string architecture = null, string qualifier = null)
{
return new RID()
{
BaseRID = baseRid,
Version = version,
OmitVersionDelimiter = OmitVersionDelimiter,
Architecture = architecture,
Qualifier = qualifier
};
}
internal IEnumerable<RIDMapping> GetRIDMappings()
{
// base =>
// Parent
yield return Parent == null ?
new RIDMapping(CreateRuntime(BaseRID)) :
new RIDMapping(CreateRuntime(BaseRID), new[] { CreateRuntime(Parent) });
foreach (var architecture in Architectures)
{
// base + arch =>
// base,
// parent + arch
var imports = new List<RID>()
{
CreateRuntime(BaseRID)
};
if (!IsNullOrRoot(Parent))
{
imports.Add(CreateRuntime(Parent, architecture: architecture));
}
yield return new RIDMapping(CreateRuntime(BaseRID, architecture: architecture), imports);
}
RuntimeVersion lastVersion = null;
foreach (var version in Versions)
{
// base + version =>
// base + lastVersion,
// parent + version (optionally)
var imports = new List<RID>()
{
CreateRuntime(BaseRID, version: lastVersion)
};
if (ApplyVersionsToParent)
{
imports.Add(CreateRuntime(Parent, version: version));
}
yield return new RIDMapping(CreateRuntime(BaseRID, version: version), imports);
foreach (var architecture in Architectures)
{
// base + version + architecture =>
// base + version,
// base + lastVersion + architecture,
// parent + version + architecture (optionally)
var archImports = new List<RID>()
{
CreateRuntime(BaseRID, version: version),
CreateRuntime(BaseRID, version: lastVersion, architecture: architecture)
};
if (ApplyVersionsToParent)
{
archImports.Add(CreateRuntime(Parent, version: version, architecture: architecture));
}
yield return new RIDMapping(CreateRuntime(BaseRID, version: version, architecture: architecture), archImports);
}
if (TreatVersionsAsCompatible)
{
lastVersion = version;
}
}
foreach (var qualifier in AdditionalQualifiers)
{
// base + qual =>
// base,
// parent + qual
yield return new RIDMapping(CreateRuntime(BaseRID, qualifier: qualifier),
new[]
{
CreateRuntime(BaseRID),
IsNullOrRoot(Parent) ? CreateRuntime(qualifier) : CreateRuntime(Parent, qualifier:qualifier)
});
foreach (var architecture in Architectures)
{
// base + arch + qualifier =>
// base + qualifier,
// base + arch
// parent + arch + qualifier
var imports = new List<RID>()
{
CreateRuntime(BaseRID, qualifier: qualifier),
CreateRuntime(BaseRID, architecture: architecture)
};
if (!IsNullOrRoot(Parent))
{
imports.Add(CreateRuntime(Parent, architecture: architecture, qualifier: qualifier));
}
yield return new RIDMapping(CreateRuntime(BaseRID, architecture: architecture, qualifier: qualifier), imports);
}
lastVersion = null;
foreach (var version in Versions)
{
// base + version + qualifier =>
// base + version,
// base + lastVersion + qualifier
// parent + version + qualifier (optionally)
var imports = new List<RID>()
{
CreateRuntime(BaseRID, version: version),
CreateRuntime(BaseRID, version: lastVersion, qualifier: qualifier)
};
if (ApplyVersionsToParent)
{
imports.Add(CreateRuntime(Parent, version: version, qualifier: qualifier));
}
yield return new RIDMapping(CreateRuntime(BaseRID, version: version, qualifier: qualifier), imports);
foreach (var architecture in Architectures)
{
// base + version + architecture + qualifier =>
// base + version + qualifier,
// base + version + architecture,
// base + version,
// base + lastVersion + architecture + qualifier,
// parent + version + architecture + qualifier (optionally)
var archImports = new List<RID>()
{
CreateRuntime(BaseRID, version: version, qualifier: qualifier),
CreateRuntime(BaseRID, version: version, architecture: architecture),
CreateRuntime(BaseRID, version: version),
CreateRuntime(BaseRID, version: lastVersion, architecture: architecture, qualifier: qualifier)
};
if (ApplyVersionsToParent)
{
imports.Add(CreateRuntime(Parent, version: version, architecture: architecture, qualifier: qualifier));
}
yield return new RIDMapping(CreateRuntime(BaseRID, version: version, architecture: architecture, qualifier: qualifier), archImports);
}
if (TreatVersionsAsCompatible)
{
lastVersion = version;
}
}
}
}
private static bool IsNullOrRoot(string rid)
{
return rid == null || rid == rootRID;
}
public IEnumerable<RuntimeDescription> GetRuntimeDescriptions()
{
foreach (var mapping in GetRIDMappings())
{
var rid = mapping.RuntimeIdentifier.ToString();
if (OmitRIDs.Contains(rid) || OmitRIDDefinitions.Contains(rid))
{
continue;
}
var imports = mapping.Imports
.Select(i => i.ToString())
.Where(i => !OmitRIDs.Contains(i) && !OmitRIDReferences.Contains(i))
.ToArray();
yield return new RuntimeDescription(rid, imports);
}
}
public RuntimeGraph GetRuntimeGraph()
{
return new RuntimeGraph(GetRuntimeDescriptions());
}
}
}

View file

@ -1,160 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Build.Framework;
using NuGet.RuntimeModel;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Microsoft.NETCore.Platforms.BuildTasks
{
public class RuntimeGroupCollection
{
private readonly ICollection<RuntimeGroup> allRuntimeGroups;
private readonly Dictionary<string, List<RuntimeGroup>> runtimeGroupsByBaseRID;
private readonly HashSet<RID> knownRIDs;
public RuntimeGroupCollection(ICollection<RuntimeGroup> runtimeGroups)
{
allRuntimeGroups = runtimeGroups;
runtimeGroupsByBaseRID = runtimeGroups.GroupBy(rg => rg.BaseRID).ToDictionary(g => g.Key, g => new List<RuntimeGroup>(g.AsEnumerable()));
knownRIDs = new HashSet<RID>(allRuntimeGroups.SelectMany(rg => rg.GetRIDMappings()).Select(mapping => mapping.RuntimeIdentifier));
}
/// <summary>
/// Locate an existing RuntimeGroup to append to.
/// Existing group must have matching baseRID, then we choose based on closest version,
/// and prefer matching arch and qualifier.
/// If no match is found, then a new RID hierarchy is created.
/// </summary>
/// <param name="runtimeIdentifier"></param>
/// <param name="parent"></param>
public void AddRuntimeIdentifier(string runtimeIdentifier, string parent)
{
// don't parse qualifier since we don't use them and they are ambiguous with `-` in base RID
RID rid = RID.Parse(runtimeIdentifier, noQualifier: true);
AddRuntimeIdentifier(rid, parent);
}
public void AddRuntimeIdentifier(RID rid, string parent)
{
// Do nothing if we already know about the RID
if (knownRIDs.Contains(rid))
{
return;
}
RuntimeGroup runtimeGroup = null;
if (runtimeGroupsByBaseRID.TryGetValue(rid.BaseRID, out var candidateRuntimeGroups))
{
RuntimeVersion closestVersion = null;
foreach (var candidate in candidateRuntimeGroups)
{
if (rid.HasVersion)
{
// Find the closest previous version
foreach (var version in candidate.Versions)
{
// a previous version
if (version <= rid.Version)
{
// haven't yet found a match or this is a closer match
if (closestVersion == null || version > closestVersion)
{
closestVersion = version;
runtimeGroup = candidate;
}
else if (version == closestVersion)
{
// found a tie in version, examine other fields
considerCandidate();
}
}
}
}
// if we don't have a version, or if we couldn't find any match, consider other fields
if (!rid.HasVersion)
{
considerCandidate();
}
// if we don't have a match yet, take this as it matches on baseRID
runtimeGroup ??= candidate;
void considerCandidate()
{
// is this a better match?
if (!rid.HasArchitecture || candidate.Architectures.Contains(rid.Architecture))
{
if (!rid.HasQualifier || candidate.AdditionalQualifiers.Contains(rid.Qualifier))
{
// matched on arch and qualifier.
runtimeGroup = candidate;
}
else if (rid.HasArchitecture && !runtimeGroup.Architectures.Contains(rid.Architecture))
{
// matched only on arch and existing match doesn't match arch
runtimeGroup = candidate;
}
}
}
}
Debug.Assert(runtimeGroup != null, "Empty candidates?");
}
else
{
// This is an unknown base RID, we'll need to add a new group.
if (string.IsNullOrEmpty(parent))
{
throw new InvalidOperationException($"AdditionalRuntimeIdentifier {rid} was specified, which could not be found in any existing {nameof(RuntimeGroup)}, and no {nameof(parent)} was specified.");
}
runtimeGroup = new RuntimeGroup(rid.BaseRID, parent);
AddRuntimeGroup(runtimeGroup);
}
runtimeGroup.ApplyRid(rid);
// Compute the portion of the RID graph produced from this modified RuntimeGroup
var ridMappings = runtimeGroup.GetRIDMappings();
// Record any newly defined RIDs in our set of known RIDs
foreach (RID definedRID in ridMappings.Select(mapping => mapping.RuntimeIdentifier))
{
knownRIDs.Add(definedRID);
}
// Make sure that any RID imported is added as well. This allows users to specify
// a single new RID and we'll add any new RIDs up the parent chain that might be needed.
foreach (RID importedRID in ridMappings.SelectMany(mapping => mapping.Imports))
{
// This should not introduce any new RuntimeGroups, so we specify parent as null
AddRuntimeIdentifier(importedRID, null);
}
}
private void AddRuntimeGroup(RuntimeGroup runtimeGroup)
{
List<RuntimeGroup> baseRuntimeGroups;
if (!runtimeGroupsByBaseRID.TryGetValue(runtimeGroup.BaseRID, out baseRuntimeGroups))
{
runtimeGroupsByBaseRID[runtimeGroup.BaseRID] = baseRuntimeGroups = new List<RuntimeGroup>();
}
baseRuntimeGroups.Add(runtimeGroup);
allRuntimeGroups.Add(runtimeGroup);
}
}
}

View file

@ -1,140 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
namespace Microsoft.NETCore.Platforms.BuildTasks
{
/// <summary>
/// A Version class that also supports a single integer (major only)
/// </summary>
public sealed class RuntimeVersion : IComparable, IComparable<RuntimeVersion>, IEquatable<RuntimeVersion>
{
private readonly string versionString;
private readonly Version version;
private readonly bool hasMinor;
public RuntimeVersion(string versionString)
{
// intentionally don't support the type of version that omits the separators as it is abiguous.
// for example Windows 8.1 was encoded as win81, where as Windows 10.0 was encoded as win10
this.versionString = versionString;
string toParse = versionString;
#if NETCOREAPP
if (!toParse.Contains('.'))
#else
if (toParse.IndexOf('.') == -1)
#endif
{
toParse += ".0";
hasMinor = false;
}
else
{
hasMinor = true;
}
version = Version.Parse(toParse);
}
public int CompareTo(object obj)
{
if (obj == null)
{
return 1;
}
if (obj is RuntimeVersion version)
{
return CompareTo(version);
}
throw new ArgumentException($"Cannot compare {nameof(RuntimeVersion)} to object of type {obj.GetType()}.", nameof(obj));
}
public int CompareTo(RuntimeVersion other)
{
if (other == null)
{
return 1;
}
int versionResult = version.CompareTo(other?.version);
if (versionResult == 0)
{
if (!hasMinor && other.hasMinor)
{
return -1;
}
if (hasMinor && !other.hasMinor)
{
return 1;
}
return string.CompareOrdinal(versionString, other.versionString);
}
return versionResult;
}
public bool Equals(RuntimeVersion other)
{
return object.ReferenceEquals(other, this) ||
(other != null &&
versionString.Equals(other.versionString, StringComparison.Ordinal));
}
public override bool Equals(object obj)
{
return Equals(obj as RuntimeVersion);
}
public override int GetHashCode()
{
return versionString.GetHashCode();
}
public override string ToString()
{
return versionString;
}
public static bool operator ==(RuntimeVersion v1, RuntimeVersion v2)
{
if (v2 is null)
{
return (v1 is null) ? true : false;
}
return ReferenceEquals(v2, v1) ? true : v2.Equals(v1);
}
public static bool operator !=(RuntimeVersion v1, RuntimeVersion v2) => !(v1 == v2);
public static bool operator <(RuntimeVersion v1, RuntimeVersion v2)
{
if (v1 is null)
{
return !(v2 is null);
}
return v1.CompareTo(v2) < 0;
}
public static bool operator <=(RuntimeVersion v1, RuntimeVersion v2)
{
if (v1 is null)
{
return true;
}
return v1.CompareTo(v2) <= 0;
}
public static bool operator >(RuntimeVersion v1, RuntimeVersion v2) => v2 < v1;
public static bool operator >=(RuntimeVersion v1, RuntimeVersion v2) => v2 <= v1;
}
}

View file

@ -0,0 +1,54 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.NETCore.Platforms
{
public class UpdateRuntimeIdentifierGraph : 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
[Required]
public ITaskItem[]? AdditionalRuntimeIdentifiers { get; set; }
public override bool Execute()
{
JToken json;
using (StreamReader streamReader = File.OpenText(InputFile!))
using (JsonTextReader jsonReader = new JsonTextReader(streamReader))
{
json = JObject.ReadFrom(jsonReader);
}
JObject runtimes = (JObject)json["runtimes"]!;
foreach (ITaskItem rid in AdditionalRuntimeIdentifiers!)
{
// Skip the RID if it's already in the graph
if (runtimes.ContainsKey(rid.ItemSpec))
{
continue;
}
string[] importedRids = rid.GetMetadata("Imports").Split(';');
runtimes.Add(rid.ItemSpec, new JObject(new JProperty("#import", new JArray(importedRids))));
}
using StreamWriter streamWriter = File.CreateText(OutputFile!);
using JsonTextWriter jsonWriter = new(streamWriter) { Formatting = Formatting.Indented };
json.WriteTo(jsonWriter);
return true;
}
}
}

View file

@ -1,296 +0,0 @@
<Project>
<ItemGroup>
<RuntimeGroup Include="unix">
<Parent>any</Parent>
<Architectures>x64;x86;arm;armv6;armel;arm64;loongarch64;mips64;s390x;ppc64le;riscv64</Architectures>
</RuntimeGroup>
<RuntimeGroup Include="linux">
<Parent>unix</Parent>
<Architectures>x64;x86;arm;armv6;armel;arm64;loongarch64;mips64;s390x;ppc64le;riscv64</Architectures>
</RuntimeGroup>
<RuntimeGroup Include="linux-musl">
<Parent>linux</Parent>
<Architectures>x64;x86;arm;armv6;armel;arm64;s390x;ppc64le;riscv64</Architectures>
</RuntimeGroup>
<RuntimeGroup Include="alpine">
<Parent>linux-musl</Parent>
<Architectures>x64;x86;arm;armv6;arm64;s390x;ppc64le</Architectures>
<Versions>3.6;3.7;3.8;3.9;3.10;3.11;3.12;3.13;3.14;3.15;3.16;3.17;3.18</Versions>
</RuntimeGroup>
<RuntimeGroup Include="linux-bionic">
<Parent>linux</Parent>
<Architectures>x64;x86;arm;arm64</Architectures>
</RuntimeGroup>
<RuntimeGroup Include="android">
<Parent>linux-bionic</Parent>
<Architectures>x64;x86;arm;arm64</Architectures>
<Versions>21;22;23;24;25;26;27;28;29;30;31;32</Versions>
</RuntimeGroup>
<RuntimeGroup Include="arch">
<Parent>linux</Parent>
<Architectures>x64</Architectures>
</RuntimeGroup>
<RuntimeGroup Include="manjaro">
<Parent>arch</Parent>
<Architectures>x64</Architectures>
</RuntimeGroup>
<RuntimeGroup Include="browser">
<Parent>any</Parent>
<Architectures>wasm</Architectures>
</RuntimeGroup>
<RuntimeGroup Include="wasi">
<Parent>any</Parent>
<Architectures>wasm</Architectures>
</RuntimeGroup>
<RuntimeGroup Include="maccatalyst">
<Parent>ios</Parent>
<Architectures>x64;arm64</Architectures>
<Versions>13;14;15</Versions>
</RuntimeGroup>
<RuntimeGroup Include="centos">
<Parent>rhel</Parent>
<Architectures>x64</Architectures>
<Versions>7</Versions>
<ApplyVersionsToParent>true</ApplyVersionsToParent>
<TreatVersionsAsCompatible>false</TreatVersionsAsCompatible>
</RuntimeGroup>
<RuntimeGroup Include="centos">
<Parent>rhel</Parent>
<Architectures>x64;arm64</Architectures>
<Versions>8;9</Versions>
<ApplyVersionsToParent>true</ApplyVersionsToParent>
<TreatVersionsAsCompatible>false</TreatVersionsAsCompatible>
</RuntimeGroup>
<RuntimeGroup Include="debian">
<Parent>linux</Parent>
<Architectures>x64;x86;arm;armel;arm64</Architectures>
<Versions>8;9;10;11;12</Versions>
<TreatVersionsAsCompatible>false</TreatVersionsAsCompatible>
</RuntimeGroup>
<RuntimeGroup Include="exherbo">
<Parent>linux</Parent>
<Architectures>x64</Architectures>
</RuntimeGroup>
<RuntimeGroup Include="fedora">
<Parent>linux</Parent>
<Architectures>x64;arm64</Architectures>
<Versions>23;24;25;26;27;28;29;30;31;32;33;34;35;36;37;38;39</Versions>
<TreatVersionsAsCompatible>false</TreatVersionsAsCompatible>
</RuntimeGroup>
<RuntimeGroup Include="gentoo">
<Parent>linux</Parent>
<Architectures>x64</Architectures>
</RuntimeGroup>
<RuntimeGroup Include="ios">
<Parent>unix</Parent>
<Architectures>arm;x86</Architectures>
<Versions>10</Versions>
</RuntimeGroup>
<RuntimeGroup Include="ios">
<Parent>unix</Parent>
<Architectures>arm64;x64</Architectures>
<Versions>10;11;12;13;14;15</Versions>
</RuntimeGroup>
<RuntimeGroup Include="iossimulator">
<Parent>ios</Parent>
<Architectures>x86</Architectures>
<Versions>10</Versions>
</RuntimeGroup>
<RuntimeGroup Include="iossimulator">
<Parent>ios</Parent>
<Architectures>arm64;x64</Architectures>
<Versions>10;11;12;13;14;15</Versions>
</RuntimeGroup>
<RuntimeGroup Include="tvos">
<Parent>unix</Parent>
<Architectures>arm64;x64</Architectures>
<Versions>10;11;12;13;14;15</Versions>
</RuntimeGroup>
<RuntimeGroup Include="tvossimulator">
<Parent>tvos</Parent>
<Architectures>arm64;x64</Architectures>
<Versions>10;11;12;13;14;15</Versions>
</RuntimeGroup>
<!-- linux mint includes major version in Include because Parent is different for each major -->
<RuntimeGroup Include="linuxmint.17">
<Parent>ubuntu.14.04</Parent>
<Architectures>x64</Architectures>
<Versions>1;2;3</Versions>
</RuntimeGroup>
<RuntimeGroup Include="linuxmint.18">
<Parent>ubuntu.16.04</Parent>
<Architectures>x64</Architectures>
<Versions>1;2;3</Versions>
</RuntimeGroup>
<RuntimeGroup Include="linuxmint.19">
<Parent>ubuntu.18.04</Parent>
<Architectures>x64</Architectures>
<Versions>1;2</Versions>
</RuntimeGroup>
<RuntimeGroup Include="miraclelinux">
<Parent>rhel</Parent>
<Architectures>x64</Architectures>
<Versions>8;9</Versions>
<ApplyVersionsToParent>true</ApplyVersionsToParent>
<TreatVersionsAsCompatible>false</TreatVersionsAsCompatible>
</RuntimeGroup>
<RuntimeGroup Include="ol">
<Parent>rhel</Parent>
<Architectures>x64</Architectures>
<Versions>7;7.0;7.1;7.2;7.3;7.4;7.5;7.6</Versions>
<ApplyVersionsToParent>true</ApplyVersionsToParent>
</RuntimeGroup>
<RuntimeGroup Include="ol">
<Parent>rhel</Parent>
<Architectures>x64</Architectures>
<Versions>8;8.0</Versions>
<ApplyVersionsToParent>true</ApplyVersionsToParent>
</RuntimeGroup>
<!-- all opensuse releases are independent -->
<RuntimeGroup Include="opensuse">
<Parent>linux</Parent>
<Architectures>x64</Architectures>
<Versions>13.2;15.0;15.1;42.1;42.2;42.3</Versions>
<TreatVersionsAsCompatible>false</TreatVersionsAsCompatible>
</RuntimeGroup>
<RuntimeGroup Include="osx">
<Parent>unix</Parent>
<Architectures>x64;arm64</Architectures>
<Versions>10.10;10.11;10.12;10.13;10.14;10.15;10.16;11.0;12;13</Versions>
</RuntimeGroup>
<RuntimeGroup Include="freebsd">
<Parent>unix</Parent>
<Architectures>x64;arm64</Architectures>
<Versions>12;13</Versions>
</RuntimeGroup>
<RuntimeGroup Include="solaris">
<Parent>unix</Parent>
<Architectures>x64</Architectures>
<Versions>11</Versions>
</RuntimeGroup>
<RuntimeGroup Include="illumos">
<Parent>unix</Parent>
<Architectures>x64</Architectures>
</RuntimeGroup>
<RuntimeGroup Include="omnios">
<Parent>illumos</Parent>
<Architectures>x64</Architectures>
<Versions>15</Versions>
</RuntimeGroup>
<RuntimeGroup Include="openindiana">
<Parent>illumos</Parent>
<Architectures>x64</Architectures>
</RuntimeGroup>
<RuntimeGroup Include="smartos">
<Parent>illumos</Parent>
<Architectures>x64</Architectures>
<Versions>2020;2021</Versions>
</RuntimeGroup>
<!-- rhel 6 is independent -->
<RuntimeGroup Include="rhel">
<Parent>linux</Parent>
<Architectures>x64</Architectures>
<Versions>6</Versions>
</RuntimeGroup>
<RuntimeGroup Include="rhel">
<Parent>linux</Parent>
<Architectures>x64</Architectures>
<Versions>7;7.0;7.1;7.2;7.3;7.4;7.5;7.6</Versions>
</RuntimeGroup>
<RuntimeGroup Include="rhel">
<Parent>linux</Parent>
<Architectures>x64;arm64</Architectures>
<Versions>8;8.0;8.1</Versions>
</RuntimeGroup>
<RuntimeGroup Include="rhel">
<Parent>linux</Parent>
<Architectures>x64;arm64</Architectures>
<Versions>9</Versions>
</RuntimeGroup>
<RuntimeGroup Include="rocky">
<Parent>rhel</Parent>
<Architectures>x64;arm64</Architectures>
<Versions>8;9</Versions>
<ApplyVersionsToParent>true</ApplyVersionsToParent>
<TreatVersionsAsCompatible>false</TreatVersionsAsCompatible>
</RuntimeGroup>
<RuntimeGroup Include="sles">
<Parent>linux</Parent>
<Architectures>x64</Architectures>
<Versions>12;12.1;12.2;12.3;12.4;15;15.1</Versions>
</RuntimeGroup>
<RuntimeGroup Include="tizen">
<Parent>linux</Parent>
<Architectures>x86;armel;arm64</Architectures>
<Versions>4.0.0;5.0.0;5.5.0;6.0.0;6.5.0;7.0.0</Versions>
</RuntimeGroup>
<RuntimeGroup Include="ubuntu">
<Parent>debian</Parent>
<Architectures>x64;x86;arm</Architectures>
<Versions>14.04;14.10;15.04;15.10</Versions>
<TreatVersionsAsCompatible>false</TreatVersionsAsCompatible>
</RuntimeGroup>
<RuntimeGroup Include="ubuntu">
<Parent>debian</Parent>
<Architectures>x64;x86;arm;arm64</Architectures>
<Versions>16.04;16.10;17.04;17.10;18.04;18.10;19.04;19.10;20.04;20.10;21.04;21.10;22.04;22.10;23.04;23.10</Versions>
<TreatVersionsAsCompatible>false</TreatVersionsAsCompatible>
</RuntimeGroup>
<RuntimeGroup Include="win">
<Parent>any</Parent>
<OmitVersionDelimiter>true</OmitVersionDelimiter>
<AdditionalQualifiers>aot</AdditionalQualifiers>
<Architectures>x64;x86;arm;arm64</Architectures>
<Versions>7;8;81;10</Versions>
</RuntimeGroup>
<RuntimeGroupWithQualifiers Include="@(RuntimeGroup)" />
<!-- root RIDs -->
<RuntimeGroupWithQualifiers Include="base" />
<RuntimeGroupWithQualifiers Include="any">
<Parent>base</Parent>
</RuntimeGroupWithQualifiers>
<!-- qualifier RIDs -->
<RuntimeGroupWithQualifiers Include="aot">
<Parent>any</Parent>
</RuntimeGroupWithQualifiers>
</ItemGroup>
</Project>

View file

@ -1,6 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Xunit;
[assembly: SkipOnPlatform(TestPlatforms.Browser, "MSBuild is not supported on Browser")]

View file

@ -1,264 +0,0 @@
// 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.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using NuGet.RuntimeModel;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.NETCore.Platforms.BuildTasks.Tests
{
// MSBuild engine is not compatible with single file
[ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.HasAssemblyFiles))]
public class GenerateRuntimeGraphTests
{
private Log _log;
private TestBuildEngine _engine;
private string defaultRootPath = (PlatformDetection.IsiOS || PlatformDetection.IstvOS) ? Path.GetTempPath() : string.Empty;
private string defaultRuntimeFile = "runtime.json";
public GenerateRuntimeGraphTests(ITestOutputHelper output)
{
_log = new Log(output);
_engine = new TestBuildEngine(_log);
if (PlatformDetection.IsiOS || PlatformDetection.IstvOS)
{
var runtimeJsonPath = Path.Combine(defaultRootPath, defaultRuntimeFile);
File.Copy(defaultRuntimeFile, runtimeJsonPath, true);
defaultRuntimeFile = runtimeJsonPath;
}
}
private static ITaskItem[] DefaultRuntimeGroupItems { get; } = GetDefaultRuntimeGroupItems();
private static ITaskItem[] GetDefaultRuntimeGroupItems()
{
Project runtimeGroupProps = new Project("runtimeGroups.props");
ITaskItem[] runtimeGroups = runtimeGroupProps.GetItems("RuntimeGroupWithQualifiers")
.Select(i => CreateItem(i)).ToArray();
Assert.NotEmpty(runtimeGroups);
return runtimeGroups;
}
private static ITaskItem CreateItem(ProjectItem projectItem)
{
TaskItem item = new TaskItem(projectItem.EvaluatedInclude);
foreach (var metadatum in projectItem.Metadata)
{
item.SetMetadata(metadatum.Name, metadatum.EvaluatedValue);
}
return item;
}
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/77889", TestPlatforms.iOS | TestPlatforms.tvOS)]
public void CanCreateRuntimeGraph()
{
// will generate and compare to existing file.
GenerateRuntimeGraph task = new GenerateRuntimeGraph()
{
BuildEngine = _engine,
RuntimeGroups = DefaultRuntimeGroupItems,
RuntimeJson = defaultRuntimeFile,
UpdateRuntimeFiles = false
};
task.Execute();
_log.AssertNoErrorsOrWarnings();
}
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/77889", TestPlatforms.iOS | TestPlatforms.tvOS)]
public void CanIgnoreExistingInferRids()
{
// will generate and compare to existing file.
GenerateRuntimeGraph task = new GenerateRuntimeGraph()
{
BuildEngine = _engine,
RuntimeGroups = DefaultRuntimeGroupItems,
RuntimeJson = defaultRuntimeFile,
AdditionalRuntimeIdentifiers = new[] { "rhel.9-x64", "centos.9-arm64", "win-x64" },
UpdateRuntimeFiles = false
};
_log.Reset();
task.Execute();
_log.AssertNoErrorsOrWarnings();
}
/// <summary>
/// Runs GenerateRuntimeGraph task specifying AdditionalRuntimeIdentifiers then asserts that the
/// generated runtime.json has the expected additions (and no more).
/// </summary>
/// <param name="additionalRIDs">additional RIDs</param>
/// <param name="expectedAdditions">entries that are expected to be added to the RuntimeGraph</param>
/// <param name="additionalRIDParent">parent to use when adding a new RID</param>
/// <param name="runtimeFilePrefix">a unique prefix to use for the generated </param>
private void AssertRuntimeGraphAdditions(string[] additionalRIDs, RuntimeDescription[] expectedAdditions, string additionalRIDParent = null, [CallerMemberName] string runtimeFilePrefix = null)
{
string runtimeFile = Path.Combine(defaultRootPath, runtimeFilePrefix + ".runtime.json");
GenerateRuntimeGraph task = new GenerateRuntimeGraph()
{
BuildEngine = _engine,
RuntimeGroups = DefaultRuntimeGroupItems,
RuntimeJson = runtimeFile,
AdditionalRuntimeIdentifiers = additionalRIDs,
AdditionalRuntimeIdentifierParent = additionalRIDParent,
UpdateRuntimeFiles = true
};
_log.Reset();
task.Execute();
_log.AssertNoErrorsOrWarnings();
RuntimeGraph expected = RuntimeGraph.Merge(
JsonRuntimeFormat.ReadRuntimeGraph(defaultRuntimeFile),
new RuntimeGraph(expectedAdditions));
RuntimeGraph actual = JsonRuntimeFormat.ReadRuntimeGraph(runtimeFile);
// Should this assert fail, it's helpful to diff defaultRuntimeFile and runtimeFile to see the additions.
Assert.Equal(expected, actual);
}
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/77889", TestPlatforms.iOS | TestPlatforms.tvOS)]
public void CanAddVersionsToExistingGroups()
{
var additionalRIDs = new[] { "ubuntu.22.04-arm64" };
var expectedAdditions = new[]
{
new RuntimeDescription("ubuntu.22.04", new[] { "ubuntu" }),
new RuntimeDescription("ubuntu.22.04-x64", new[] { "ubuntu.22.04", "ubuntu-x64" }),
new RuntimeDescription("ubuntu.22.04-x86", new[] { "ubuntu.22.04", "ubuntu-x86" }),
new RuntimeDescription("ubuntu.22.04-arm", new[] { "ubuntu.22.04", "ubuntu-arm" }),
new RuntimeDescription("ubuntu.22.04-arm64", new[] { "ubuntu.22.04", "ubuntu-arm64" })
};
AssertRuntimeGraphAdditions(additionalRIDs, expectedAdditions);
}
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/77889", TestPlatforms.iOS | TestPlatforms.tvOS)]
public void CanAddParentVersionsToExistingGroups()
{
var additionalRIDs = new[] { "centos.9.2-arm64" };
var expectedAdditions = new[]
{
new RuntimeDescription("centos.9.2", new[] { "centos", "rhel.9.2" }),
new RuntimeDescription("centos.9.2-x64", new[] { "centos.9.2", "centos-x64", "rhel.9.2-x64" }),
new RuntimeDescription("centos.9.2-arm64", new[] { "centos.9.2", "centos-arm64", "rhel.9.2-arm64" }),
// rhel RIDs are implicitly created since centos imports versioned RHEL RIDs
new RuntimeDescription("rhel.9.2", new[] { "rhel.9" }),
new RuntimeDescription("rhel.9.2-x64", new[] { "rhel.9.2", "rhel.9-x64" }),
new RuntimeDescription("rhel.9.2-arm64", new[] { "rhel.9.2", "rhel.9-arm64" })
};
AssertRuntimeGraphAdditions(additionalRIDs, expectedAdditions);
}
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/77889", TestPlatforms.iOS | TestPlatforms.tvOS)]
public void CanAddMajorVersionsToExistingGroups()
{
var additionalRIDs = new[] { "rhel.10-x64" };
var expectedAdditions = new[]
{
// Note that rhel doesn't treat major versions as compatible, however we do since it's closest and we don't represent this policy in the RuntimeGroups explicitly.
// We could add a rule that wouldn't insert a new major version if we see existing groups are split by major version.
new RuntimeDescription("rhel.10", new[] { "rhel.9" }),
new RuntimeDescription("rhel.10-x64", new[] { "rhel.10", "rhel.9-x64" }),
new RuntimeDescription("rhel.10-arm64", new[] { "rhel.10", "rhel.9-arm64" })
};
AssertRuntimeGraphAdditions(additionalRIDs, expectedAdditions);
}
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/77889", TestPlatforms.iOS | TestPlatforms.tvOS)]
public void CanAddArchitectureToExistingGroups()
{
var additionalRIDs = new[] { "win10-x128" };
var expectedAdditions = new[]
{
new RuntimeDescription("win10-x128", new[] { "win10", "win81-x128" }),
new RuntimeDescription("win10-x128-aot", new[] { "win10-aot", "win10-x128", "win10", "win81-x128-aot" }),
new RuntimeDescription("win81-x128-aot", new[] { "win81-aot", "win81-x128", "win81", "win8-x128-aot" }),
new RuntimeDescription("win81-x128", new[] { "win81", "win8-x128" }),
new RuntimeDescription("win8-x128-aot", new[] { "win8-aot", "win8-x128", "win8", "win7-x128-aot" }),
new RuntimeDescription("win8-x128", new[] { "win8", "win7-x128" }),
new RuntimeDescription("win7-x128-aot", new[] { "win7-aot", "win7-x128", "win7", "win-x128-aot" }),
new RuntimeDescription("win7-x128", new[] { "win7", "win-x128" }),
new RuntimeDescription("win-x128-aot", new[] { "win-aot", "win-x128" }),
new RuntimeDescription("win-x128", new[] { "win" })
};
AssertRuntimeGraphAdditions(additionalRIDs, expectedAdditions);
}
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/77889", TestPlatforms.iOS | TestPlatforms.tvOS)]
public void CanAddArchitectureAndVersionToExistingGroups()
{
var additionalRIDs = new[] { "osx.13-powerpc" };
var expectedAdditions = new[]
{
new RuntimeDescription("osx.13-powerpc", new[] { "osx.13", "osx.12-powerpc" }),
new RuntimeDescription("osx.13-arm64", new[] { "osx.13", "osx.12-arm64" }),
new RuntimeDescription("osx.13-x64", new[] { "osx.13", "osx.12-x64" }),
new RuntimeDescription("osx.13", new[] { "osx.12" }),
// our RID model doesn't give priority to architecture, so the new architecture is applied to all past versions
new RuntimeDescription("osx.12-powerpc", new[] { "osx.12", "osx.11.0-powerpc" }),
new RuntimeDescription("osx.11.0-powerpc", new[] { "osx.11.0", "osx.10.16-powerpc" }),
new RuntimeDescription("osx.10.16-powerpc", new[] { "osx.10.16", "osx.10.15-powerpc" }),
new RuntimeDescription("osx.10.15-powerpc", new[] { "osx.10.15", "osx.10.14-powerpc" }),
new RuntimeDescription("osx.10.14-powerpc", new[] { "osx.10.14", "osx.10.13-powerpc" }),
new RuntimeDescription("osx.10.13-powerpc", new[] { "osx.10.13", "osx.10.12-powerpc" }),
new RuntimeDescription("osx.10.12-powerpc", new[] { "osx.10.12", "osx.10.11-powerpc" }),
new RuntimeDescription("osx.10.11-powerpc", new[] { "osx.10.11", "osx.10.10-powerpc" }),
new RuntimeDescription("osx.10.10-powerpc", new[] { "osx.10.10", "osx-powerpc" }),
new RuntimeDescription("unix-powerpc", new[] { "unix" }),
new RuntimeDescription("osx-powerpc", new[] { "osx", "unix-powerpc" }),
};
AssertRuntimeGraphAdditions(additionalRIDs, expectedAdditions);
}
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/77889", TestPlatforms.iOS | TestPlatforms.tvOS)]
public void CanAddNewGroups()
{
var additionalRIDs = new[] { "yolinux.42.0-quantum" };
var expectedAdditions = new[]
{
new RuntimeDescription("unix-quantum", new[] { "unix" }),
new RuntimeDescription("linux-quantum", new[] { "linux", "unix-quantum" }),
new RuntimeDescription("linux-musl-quantum", new[] { "linux-musl", "linux-quantum" }),
new RuntimeDescription("yolinux", new[] { "linux-musl" }),
new RuntimeDescription("yolinux-quantum", new[] { "yolinux", "linux-musl-quantum" }),
new RuntimeDescription("yolinux.42.0", new[] { "yolinux" }),
new RuntimeDescription("yolinux.42.0-quantum", new[] { "yolinux.42.0", "yolinux-quantum" })
};
AssertRuntimeGraphAdditions(additionalRIDs, expectedAdditions, "linux-musl");
}
}
}

View file

@ -1,56 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.NETCore.Platforms.BuildTasks.Tests
{
internal class Log : ILog
{
private readonly ITestOutputHelper _output;
public Log(ITestOutputHelper output)
{
_output = output;
Reset();
}
public int ErrorsLogged { get; set; }
public int WarningsLogged { get; set; }
public void LogError(string message, params object[] messageArgs)
{
ErrorsLogged++;
_output.WriteLine("Error: " + message, messageArgs);
}
public void LogMessage(string message, params object[] messageArgs)
{
_output.WriteLine(message, messageArgs);
}
public void LogMessage(LogImportance importance, string message, params object[] messageArgs)
{
_output.WriteLine(message, messageArgs);
}
public void LogWarning(string message, params object[] messageArgs)
{
WarningsLogged++;
_output.WriteLine("Warning: " + message, messageArgs);
}
public void Reset()
{
ErrorsLogged = 0;
WarningsLogged = 0;
}
public void AssertNoErrorsOrWarnings()
{
Assert.Equal(0, ErrorsLogged);
Assert.Equal(0, WarningsLogged);
}
}
}

View file

@ -1,29 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetFrameworkToolCurrent)</TargetFrameworks>
<IgnoreForCI Condition="'$(TargetOS)' == 'browser'">true</IgnoreForCI>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\src\Microsoft.NETCore.Platforms.csproj" />
<PackageReference Include="Microsoft.Build" Version="$(MicrosoftBuildVersion)" />
<!-- Workaround NuGet promoting this to a ProjectReference -->
<PackageReference Include="System.Memory" Condition="'$(TargetFramework)' == 'net472'" Version="$(SystemMemoryVersion)" />
<PackageReference Include="System.Security.AccessControl" Condition="'$(TargetFramework)' == 'net472'" Version="$(SystemSecurityAccessControlVersion)" />
<PackageReference Include="System.Security.Principal.Windows" Condition="'$(TargetFramework)' == 'net472'" Version="$(SystemSecurityPrincipalWindowsVersion)" />
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="GenerateRuntimeGraphTests.cs" />
<Compile Include="Log.cs" />
<Compile Include="RidTests.cs" />
<Compile Include="RuntimeVersionTests.cs" />
<Compile Include="TestBuildEngine.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="..\src\runtimeGroups.props" CopyToOutputDirectory="PreserveNewest" />
<Content Include="..\src\runtime.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>

View file

@ -1,66 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using Xunit;
namespace Microsoft.NETCore.Platforms.BuildTasks.Tests
{
public class RidTests
{
public static IEnumerable<object[]> ValidRIDData()
{
yield return new object[] { "win10-x64", new RID() { BaseRID = "win", OmitVersionDelimiter = true, Version = new RuntimeVersion("10"), Architecture = "x64" }, null };
yield return new object[] { "win10", new RID() { BaseRID = "win", OmitVersionDelimiter = true, Version = new RuntimeVersion("10")}, null };
yield return new object[] { "linux", new RID() { BaseRID = "linux" }, null };
yield return new object[] { "linux-x64", new RID() { BaseRID = "linux", Architecture = "x64" }, null };
yield return new object[] { "linux-x64", new RID() { BaseRID = "linux", Architecture = "x64" }, null };
yield return new object[] { "debian.10-x64", new RID() { BaseRID = "debian", Version = new RuntimeVersion("10"), Architecture = "x64" }, null };
yield return new object[] { "linuxmint.19.2-x64", new RID() { BaseRID = "linuxmint", Version = new RuntimeVersion("19.2"), Architecture = "x64" }, null };
yield return new object[] { "ubuntu.14.04-x64", new RID() { BaseRID = "ubuntu", Version = new RuntimeVersion("14.04"), Architecture = "x64" }, null };
yield return new object[] { "foo-bar.42-arm", new RID() { BaseRID = "foo-bar", Version = new RuntimeVersion("42"), Architecture = "arm" }, null };
yield return new object[] { "foo-bar-arm", new RID() { BaseRID = "foo", Architecture = "bar", Qualifier = "arm" }, // demonstrates ambiguity, avoid using `-` in base
new RID() { BaseRID = "foo-bar", Architecture = "arm" } };
yield return new object[] { "linux-musl-x64", new RID() { BaseRID = "linux", Architecture = "musl", Qualifier = "x64" }, // yes, we already have ambiguous RIDs
new RID() { BaseRID = "linux-musl", Architecture = "x64" } };
}
[Theory]
[MemberData(nameof(ValidRIDData))]
internal void ParseCorrectly(string input, RID expected, RID? expectedNoQualifier)
{
_ = expectedNoQualifier; // unused
RID actual = RID.Parse(input, noQualifier: false);
Assert.Equal(expected, actual);
}
[Theory]
[MemberData(nameof(ValidRIDData))]
internal void ParseCorrectlyNoQualifier(string input, RID expected, RID? expectedNoQualifier)
{
expectedNoQualifier ??= expected;
RID actual = RID.Parse(input, noQualifier: true);
Assert.Equal(expectedNoQualifier, actual);
}
[Theory]
[MemberData(nameof(ValidRIDData))]
internal void ToStringAsExpected(string expected, RID rid, RID? expectedNoQualifierRid)
{
string actual = rid.ToString();
Assert.Equal(expected, actual);
if (expectedNoQualifierRid is not null)
{
actual = expectedNoQualifierRid.ToString();
Assert.Equal(expected, actual);
}
}
}
}

View file

@ -1,233 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using Xunit;
namespace Microsoft.NETCore.Platforms.BuildTasks.Tests
{
public class RuntimeVersionTests
{
public enum Comparison
{
LessThan,
Equal,
GreaterThan
}
public static IEnumerable<object[]> ComparisonData()
{
yield return new object[] { "0.0", "00.0", Comparison.LessThan };
yield return new object[] { "2.0", "1.0", Comparison.GreaterThan };
yield return new object[] { "2", "1.0", Comparison.GreaterThan };
yield return new object[] { "2", "1", Comparison.GreaterThan };
yield return new object[] { "10", "10.0", Comparison.LessThan };
yield return new object[] { "10", "10.00", Comparison.LessThan };
yield return new object[] { "10.0", "10.0", Comparison.Equal };
yield return new object[] { "10.0", null, Comparison.GreaterThan };
yield return new object[] { "8", "8", Comparison.Equal };
}
[MemberData(nameof(ComparisonData))]
[Theory]
public static void CompareTo(string vs1, string vs2, Comparison expected)
{
RuntimeVersion v1 = new RuntimeVersion(vs1);
RuntimeVersion v2 = vs2 == null ? null : new RuntimeVersion(vs2);
int actual = v1.CompareTo(v2);
int invActual = v2?.CompareTo(v1) ?? -1;
switch (expected)
{
case Comparison.LessThan:
Assert.True(actual < 0);
Assert.True(invActual > 0);
break;
case Comparison.Equal:
Assert.Equal(0, actual);
Assert.Equal(0, invActual);
break;
case Comparison.GreaterThan:
Assert.True(actual > 0);
Assert.True(invActual < 0);
break;
}
}
[MemberData(nameof(ComparisonData))]
[Theory]
public static void GreaterThan(string vs1, string vs2, Comparison expected)
{
RuntimeVersion v1 = new RuntimeVersion(vs1);
RuntimeVersion v2 = vs2 == null ? null : new RuntimeVersion(vs2);
bool actual = v1 > v2;
bool invActual = v2 > v1;
switch (expected)
{
case Comparison.LessThan:
Assert.False(actual);
Assert.True(invActual);
break;
case Comparison.Equal:
Assert.False(actual);
Assert.False(invActual);
break;
case Comparison.GreaterThan:
Assert.True(actual);
Assert.False(invActual);
break;
}
}
[MemberData(nameof(ComparisonData))]
[Theory]
public static void GreaterThanOrEqual(string vs1, string vs2, Comparison expected)
{
RuntimeVersion v1 = new RuntimeVersion(vs1);
RuntimeVersion v2 = vs2 == null ? null : new RuntimeVersion(vs2);
bool actual = v1 >= v2;
bool invActual = v2 >= v1;
switch (expected)
{
case Comparison.LessThan:
Assert.False(actual);
Assert.True(invActual);
break;
case Comparison.Equal:
Assert.True(actual);
Assert.True(invActual);
break;
case Comparison.GreaterThan:
Assert.True(actual);
Assert.False(invActual);
break;
}
}
[MemberData(nameof(ComparisonData))]
[Theory]
public static void LessThan(string vs1, string vs2, Comparison expected)
{
RuntimeVersion v1 = new RuntimeVersion(vs1);
RuntimeVersion v2 = vs2 == null ? null : new RuntimeVersion(vs2);
bool actual = v1 < v2;
bool invActual = v2 < v1;
switch (expected)
{
case Comparison.LessThan:
Assert.True(actual);
Assert.False(invActual);
break;
case Comparison.Equal:
Assert.False(actual);
Assert.False(invActual);
break;
case Comparison.GreaterThan:
Assert.False(actual);
Assert.True(invActual);
break;
}
}
[MemberData(nameof(ComparisonData))]
[Theory]
public static void LessThanOrEqual(string vs1, string vs2, Comparison expected)
{
RuntimeVersion v1 = new RuntimeVersion(vs1);
RuntimeVersion v2 = vs2 == null ? null : new RuntimeVersion(vs2);
bool actual = v1 <= v2;
bool invActual = v2 <= v1;
switch (expected)
{
case Comparison.LessThan:
Assert.True(actual);
Assert.False(invActual);
break;
case Comparison.Equal:
Assert.True(actual);
Assert.True(invActual);
break;
case Comparison.GreaterThan:
Assert.False(actual);
Assert.True(invActual);
break;
}
}
[MemberData(nameof(ComparisonData))]
[Theory]
public static void Equal(string vs1, string vs2, Comparison expected)
{
RuntimeVersion v1 = new RuntimeVersion(vs1);
RuntimeVersion v2 = vs2 == null ? null : new RuntimeVersion(vs2);
bool actual = v1 == v2;
bool invActual = v2 == v1;
switch (expected)
{
case Comparison.LessThan:
Assert.False(actual);
Assert.False(invActual);
break;
case Comparison.Equal:
Assert.True(actual);
Assert.True(invActual);
break;
case Comparison.GreaterThan:
Assert.False(actual);
Assert.False(invActual);
break;
}
}
[MemberData(nameof(ComparisonData))]
[Theory]
public static void GetHashCodeUnique(string vs1, string vs2, Comparison expected)
{
RuntimeVersion v1 = new RuntimeVersion(vs1);
RuntimeVersion v2 = vs2 == null ? null : new RuntimeVersion(vs2);
int h1 = v1.GetHashCode();
int h2 = v2?.GetHashCode() ?? 0;
switch (expected)
{
case Comparison.LessThan:
Assert.NotEqual(h1, h2);
break;
case Comparison.Equal:
Assert.Equal(h1, h2);
break;
case Comparison.GreaterThan:
Assert.NotEqual(h1, h2);
break;
}
}
public static IEnumerable<object[]> ValidVersions()
{
yield return new object[] { "0" };
yield return new object[] { "00" };
yield return new object[] { "000" };
yield return new object[] { "1" };
yield return new object[] { "1.0" };
yield return new object[] { "1.1" };
yield return new object[] { "1.01" };
yield return new object[] { "1.2.3.4" };
yield return new object[] { "1.02.03.04" };
}
[MemberData(nameof(ValidVersions))]
[Theory]
public static void RoundTripToString(string expected)
{
RuntimeVersion version = new RuntimeVersion(expected);
string actual = version.ToString();
Assert.Equal(expected, actual);
}
}
}

View file

@ -1,56 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Build.Framework;
using System;
using System.Collections;
namespace Microsoft.NETCore.Platforms.BuildTasks.Tests
{
public class TestBuildEngine : IBuildEngine
{
private ILog _log;
public TestBuildEngine(ILog log)
{
ColumnNumberOfTaskNode = 0;
ContinueOnError = true;
LineNumberOfTaskNode = 0;
ProjectFileOfTaskNode = "test";
_log = log;
}
public int ColumnNumberOfTaskNode { get; set; }
public bool ContinueOnError { get; set; }
public int LineNumberOfTaskNode { get; set; }
public string ProjectFileOfTaskNode { get; set; }
public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs)
{
throw new NotImplementedException();
}
public void LogCustomEvent(CustomBuildEventArgs e)
{
_log.LogMessage(e.Message);
}
public void LogErrorEvent(BuildErrorEventArgs e)
{
_log.LogError(e.Message);
}
public void LogMessageEvent(BuildMessageEventArgs e)
{
_log.LogMessage((LogImportance)e.Importance, e.Message);
}
public void LogWarningEvent(BuildWarningEventArgs e)
{
_log.LogWarning(e.Message);
}
}
}

View file

@ -341,7 +341,6 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetOS)' == 'browser' and '$(RunDisabledWasmTests)' != 'true'">
<ProjectExclusions Include="$(MSBuildThisFileDirectory)Microsoft.NETCore.Platforms\tests\Microsoft.NETCore.Platforms.Tests.csproj" />
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Runtime.InteropServices.JavaScript\tests\JSImportGenerator.UnitTest\JSImportGenerator.Unit.Tests.csproj" />
<!-- This test is disabled via an assembly-level attribute in source. We exclude it here to avoid queuing/running a work item entirely.