question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Proposal for platform-specific apps for .NET 8

See original GitHub issue

Proposal for platform-specific apps for .NET 8

We’ve been on a journey to rationalize dotnet build and dotnet publish in terms of producing platform-specific apps and how the CLI exposes a natural progression of app specialization options. The overall product started out without sufficient design on this topic and we’ve been trying to remedy that ever since. We believe we can finally correct these design challenges with .NET 8.

Notes:

  • build is used a short-hand for build and publish unless specifically called out otherwise.
  • “platform-specific” is used as a synonym for “RID-specific”.
  • Intended as a concrete plan for much of https://github.com/dotnet/sdk/issues/26446.

Context

Framework-dependent apps have been the default build output type since .NET Core 1.0. That remains the best default in our view. The primary deployment model for .NET is a globally installed runtime, and framework-dependent apps align with that. We should retain this behavior.

In many scenarios, an app will only ever run in one platform environment (like Linux + x64), and developers know that a priori (for example for containers or client apps). There can be performance benefits by taking advantage of that. In addition, apps built for a single environment are simpler (fewer assets and flat directory structure). We should make it easier to produce platform-specific apps, ideally making it the default.

In contrast, in other scenarios, developers benefit from apps being portable across operating system and architectures. That model has been the default to date and is arguably an advanced pseudo-magic behavior. This behavior should be opt-in since it comes with important trade-offs that developers should understand (or that publishing workflows provide as a requirement).

Platform specialization

Developers can specialize an app for a given platform by specifying a Runtime ID (RID) when building an app, with the -r argument, like with:

dotnet build -r linux-x64

Specializing an app with a RID produces a self-contained app. This design choice has always been unfortunate, since RID specialization equally applies to framework-dependent and self-contained apps.

The SDK provides a warning when a RID is specified without any additional arguments related to output type. This warning was added with .NET 6.

root@efbeccd6a45b:/app# dotnet build -r linux-arm64
MSBuild version 17.4.1+9a89d02ff for .NET
  Determining projects to restore...
  Restored /app/app.csproj (in 5.16 sec).
/usr/share/dotnet/sdk/7.0.102/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.Sdk.targets(1142,5): warning NETSDK1179: One of '--self-contained' or '--no-self-contained' options are required when '--runtime' is used. [/app/app.csproj]

The intent of this warning was enabling an eventual change in the default behavior when specifying a RID. Given that the warning has been in place for two versions, including an LTS version, we can reasonably now make the change.

Starting with apps that target .NET 8:

  • Building with a RID will produce a framework-dependent app.
  • If --self-contained or --self-contained true is specified, then a self-contained app will be produced. Same when the SelfContained=true.
  • Warning NETSDK1179 will no longer be produced.
  • For earlier versions, We should consider changing the wording of the warning to make clear that the behavior has changed for .NET 8+ apps.

As the current warning suggests, developers should specify --self-contained in response for this change, if they haven’t already.

Platform specializing by default

We can further refine this proposal by specializing apps by default. We also need to consider compatibility, and an opt-out to enable producing portable apps.

In fact, there is already a CLI argument that provides this behavior:

root@efbeccd6a45b:/app# dotnet build --help | grep runtime
  --use-current-runtime                Use current runtime as the target 
                                       runtime.

We could add a property with that name, but we can probably do better.

Instead, we propose a TargetRuntime property that pairs with TargetFramework. It will be a convenience wrapper over RuntimeIdentifier, meaning that it will always produce a valid value (including empty string) for that property.

TargetRuntime will allow the following values:

  • portable – same as specifying no RID and pro
  • current – same as specifying --use-current-runtime
  • any valid RID

.NET 8 templates will be updated to include this property, set to current, making .NET 8 apps RID-specific by default.

The follow project file demonstrates the change for the console template.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <TargetRuntime>current</TargetRuntime>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

We are aware that there are two variants of “current RID”, one short, one long.

The --use-current-runtime flag results in an OS-specific RID, like osx-arm64, while dotnet --info prints a RID with a version, as follows.

% dotnet --info | grep RID
 RID:         osx.13-arm64

That seems odd and asymmetric. Ideally, both variants would be accessible from within a project file and the CLI. More generally, we don’t have good names for these different styles of RIDs. We don’t need to resolve that for this feature, but it is something that we should separately look into.

Portable apps and apphost

Portable apps include the apphost by default. That doesn’t really make sense since the apphost is platform-specific, while the rest of the app is intended to work anywhere. However, it’s possible that developers appreciate having an apphost for local dev.

There is no harm to shipping an apphost with a portable app. It is very small and very unlikely to be the source of a vulnerability. However, the platform it supports is arbitrary with respect to the platforms it might run on. As a result, it is hard to describe a general use case (beyond development).

We could adopt the following model:

  • Portable apps do not include an apphost, the same behavior as early .NET Core versions.
  • Developers that want an apphost (the existing behavior) can set UseAppHost=true.
  • Developer that want an apphost only for development can set UseDebugAppHost=true (which would be a new property). An alternative would be UseAppHost=Debug.

As an aside, we notice many Dockerfile examples where apps are launched with the dotnet foo.dll pattern, even those they don’t need to. Lauching an app with the dotnet launcher is a fine pattern and should certainly be embraced by portable apps, since it’s the only correct cross-platform pattern.

@nagilson @DamianEdwards @baronfel @dsplaisted @elinor-fung

Issue Analytics

  • State:open
  • Created 8 months ago
  • Reactions:2
  • Comments:10 (9 by maintainers)

github_iconTop GitHub Comments

2reactions
richlandercommented, Jan 20, 2023

The portable apphost change isn’t about space saving. It’s about defining the scenario for the apphost (for portable apps). I claim that it is very narrow and that having it confuses things. If it wasn’t there, it would help people better understand what portable apps are for.

Perhaps it isn’t obvious, but the apphost is NOT portable.

1reaction
nagilsoncommented, Jul 11, 2023

Damian’s recollection is correct! Though the idea of doing it in .NET 9 is very up in the air.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Public Preview: App Service support for .NET 8
NET 8 and run web apps in Azure App Service (for both Windows and Linux. App Service provides early access to new language...
Read more >
All things client and mobile app development with .NET MAUI
Code, test, ship, and continuously improve your mobile and desktop applications with the latest release of . NET Multi- platform App UI (MAUI) ......
Read more >
Native Cross-Platform Mobile & Desktop Apps with .NET MAUI
Speaker: James Montemagno . NET MAUI is the best way to build native cross-platform mobile and desktop apps with . NET and C#....
Read more >
Let's Learn .NET - .NET MAUI (cross-platform apps in C#)
In this month's . NET beginner series, get a full introduction to build native, cross-platform desktop and mobile apps with . NET !...
Read more >
8 cross-platform app development frameworks
Cross-platform app frameworks allow developers to create mobile apps with one-time coding, and without a lot of changes run it on different platforms....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found