What does the win/any runtime mean in .NET Core

RIDs are used with .NET Core to resolve dependencies on packages. The root for this process of resolving dependencies is your project, which you explicitly tag with one or more RIDs. When building the project, you indicate which RID you are building against.

RIDs are defined in a forest of compatibility trees, where any node in a tree represents an execution environment that can support all of its children. Each RID is the root of such a tree.

Here is an example RID compatibility tree:

win10-x64
|- win10
|  `- win81
|     `- win8
|        `- win7
|           `- win
|              `- any
|                 `- base
`- win81-x64
   |- win81 (already included above)
   `- win8-x64
      |- win8 (already included above)
      `- win7-x64
         |- win7 (already included above)
         `- win-x64
            `- win (already included above)

The full graph of RID compatibility trees is defined here:

https://github.com/dotnet/runtime/blob/master/src/libraries/pkg/Microsoft.NETCore.Platforms/runtime.json

A package can supply a different implementation for every RID if necessary. When building, if I have a dependency on that package, the build process will select the implementation closest to the root of the tree. If the tree doesn't contain any RIDs supplied by the package, then the build will fail.

There is a special kind of package called a "runtime package". Runtime packages contain native binaries that be directly loaded and executed by the host operating system. As such, these packages only supply implementations for concrete OS versions: "win7-x64", for instance, but not "win7" or "win-x64", and, say, "ubuntu.16.04-x64", but not "ubuntu.16.04", "ubuntu-x64" or "linux".

[Update: as of .NET Core 2.0, you can build for Linux-x64 to target "all" x64 versions of Linux with a single build. See https://blogs.msdn.microsoft.com/dotnet/2017/08/14/announcing-net-core-2-0/ ]

Runtime packages come into play when bundling stand-alone projects. With a stand-alone project, everything needed to run the project must be included in the build output. This means the build output must include a native binary as the entrypoint for the application. That native binary is supplied by the runtime package.

So, to address your questions:

  1. If I specify a RID of win7-x32 will my code also run on a 64 bit Windows OS?

Yes it will, but it will run in a 32-bit process. I have verified this with an app built & published from an Ubuntu dev VM and subsequently run on Windows 10 64-bit; if the app is published against win7-x32, then IntPtr.Size is 4, and if it is published against win7-x64, then IntPtr.Size is 8. It runs either way.

The win7-x32 runtime package includes a 32-bit EXE file that hosts the .NET Core runtime and then loads & runs your project, which is bundled alongside it in a DLL file with the same name.

  1. If I specify a RID of win7, what does it build, will it build the 32 bit version or the 64 bit version?

If you specify a RID of win7, it will try to find native binary builds tagged with that RID, or a compatible RID, but it won't find any. The build will fail, because there is no "win7" version of the main entrypoint EXE. You must specify either 32-bit or 64-bit (and it looks like all other platforms are 64-bit only).

I have tested this specific detail, and have found that:

  • The dotnet restore step does not fail, but also does not install a runtime for win7 (or win10).

  • The dotnet build step succeeds in compiling the test application, but then emits this error:

    Failed to make the following project runnable: helloworld (.NETCoreApp,Version=v1.1) reason: Expected coreclr library not found in package graph. Please try running dotnet restore again.

  1. If I specify a RID of win7, will my program run in Windows 8, 8.1, or 10?

Assuming you specify either win7-x86 or win7-x64, then yes. The win7-x86 or win7-x64 runtime package will supply an EXE entrypoint that is a 32-bit or 64-bit EXE, respectively, and those EXEs are native binaries that will run on any Windows version starting with Windows 7.

Note that there is no runtime package currently for Windows 8, Windows 8.1 or Windows 10 specifically. The compatibility graphs for newer Windows versions includes either win7-x86 or win7-x64, as appropriate, and so that particular runtime package ends up being used in the build, even if you target a newer RID such as win10-x64.

  1. What does the any RID do? I understand how the portable deployment can be used on multiple platforms but how can the standalone deployment (built with a RID of any) work on Linux as well as Windows? Am I misunderstanding this RID?

The any RID allows a package to supply an implementation for any RID further up the chain, because all other RIDs ultimately include any (and base) in their compatibility tree. Runtime packages, though, do not supply any implementation for any, and thus any cannot be used to build stand-alone packages.

  1. If I specify a RID of blah I expected an error. Instead my application was built in the bin/Release/blah directory. Did it simply default to some other runtime?

Your project must be configured with "type": "platform" in the dependency on Microsoft.NETCore.App. As such, no stand-alone package was built, and the resolution of supporting libraries is left to runtime, at which point the RID is supplied by the actual runtime you are using to run your app, rather than by your application's build configuration.

If your project is a library, then when you try to reference it from another project, you may encounter problems because your library is only supplying an implementation for the "blah" platform, which won't be in the compatibility tree for the RID the other project is building against. If your project is an application, then blah is being ignored.

If you reconfigure your project to produce a stand-alone package (by removing or commenting out the "type": "platform" line in project.json), you will find that it no longer builds, because it now has a dependency on the runtime packages, and there is no package for RID blah.


I believe the official documentation linked to in the OP provided all the necessary information.

First things first

What are RIDs?

RID is short for Runtime IDentifier. RIDs are used to identify target operating systems where an application or asset (that is, assembly) will run.

It is important to note that RIDs are really opaque strings. This means that they have to match exactly for operations using them to work.

This was also quoted from GitHub

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.

Best RID Consider the partial RID-graph:

"any": {},

"win": {
    "#import": [ "any" ]
},
"win-x86": {
    "#import": [ "win" ]
},
"win-x64": {
    "#import": [ "win" ]
},
"win7": {
    "#import": [ "win" ]
},
"win7-x86": {
    "#import": [ "win7", "win-x86" ]
},
"win7-x64": {
    "#import": [ "win7", "win-x64" ]
}

This can be visualized as a directed graph, as follows:

win7-x64    win7-x86
   |   \   /    |
   |   win7     |
   |     |      |
win-x64  |  win-x86
      \  |  /
        win
         |
        any

As such, best RID, when evaluating for win7-x64 would be:win7-x64, win7, win-x64, win, any Similarly, when evaluating for win-x64: win-x64, win, any Note that win7 comes before win-x64 due to the import for win7 appearing before the import for win-x64 in document order.

That said, and referencing runtime.json on the CoreFX repo.

If you use this file, you will notice that some of the RIDs have an "#import" statement in them. These statements are compatibility statements. That means that a RID that has an imported RID in it can be a target for restoring packages for that RID.

Extracting only the relevant parts,

1) If I specify a RID of win7-x32 will my code also run on a 64 bit Windows OS?

"base": {
},

"any": {
    "#import": [ "base" ]
},
...
"win": {
    "#import": [ "any" ]
},
...
"win7": {
        "#import": [ "win" ]
    },
"win7-x86": {
    "#import": [ "win7", "win-x86" ]
},
"win7-x64": {
    "#import": [ "win7", "win-x64" ]
},
...

2) If I specify a RID of win7, what does it build, will it build the 32 bit version or the 64 bit version?

It will build a common version that can run on both platforms. Refer to visualization above.

3) If I specify a RID of win7, will my program run in Windows 8, 8.1, or 10?

Yes. Based on the imports of the referenced versions.

"win8": {
    "#import": [ "win7" ]
},
"win8-x86": {
    "#import": [ "win8", "win7-x86" ]
},
"win8-x64": {
    "#import": [ "win8", "win7-x64" ]
},
"win8-arm": {
    "#import": [ "win8" ]
},

"win81": {
    "#import": [ "win8" ]
},
"win81-x86": {
    "#import": [ "win81", "win8-x86" ]
},
"win81-x64": {
    "#import": [ "win81", "win8-x64" ]
},
"win81-arm": {
    "#import": [ "win81", "win8-arm" ]
},

"win10": {
    "#import": [ "win81" ]
},
"win10-x86": {
    "#import": [ "win10", "win81-x86" ]
},
"win10-x64": {
    "#import": [ "win10", "win81-x64" ]
},

4) What does the any RID do?

It means that the build is compatible with any of the supported platforms and it can be a target for restoring packages for any RID.

5) If I specify a RID of blah I expected an error. Instead my application was built in the bin/Release/blah directory. Did it simply default to some other runtime?

Quoting form documentation:

All RIDs eventually map back to the root any RID.

And finally, again from documentation, take note

Although they look easy enough to use, there are some special things about RIDs that you have to keep in mind when working with them:

  • They are opaque strings and should be treated as black boxes
    • You should not construct RIDs programmatically
  • You need to use the RIDs that are already defined for the platform and this document shows that
  • The RIDs do need to be specific so don't assume anything from the actual RID value; please consult this document to determine which RID(s) you need for a given platform

Under .NET Core 2.0, its enough to build for the following targets:

  • linux-x64, linux-arm
  • win-x64, win-x86
  • osx-x64

See https://blogs.msdn.microsoft.com/dotnet/2017/08/14/announcing-net-core-2-0/