Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

.NET 8: [breaking] dotnet build/publish produces RID-specific apps by default #23540

Closed
richlander opened this issue Jan 21, 2022 · 7 comments
Closed
Labels
Area-NetSDK untriaged Request triage from a team member
Milestone

Comments

@richlander
Copy link
Member

richlander commented Jan 21, 2022

[breaking] dotnet build/publish produces RID-specific apps by default

Proposal: dotnet build (and friends) produce RID-specific apps by default. This change results in apps that are smaller, simpler, faster to startup, and more reliable.

Proposal: any is the new RID for portable apps, since they will no longer be the default.

Example:

PS C:\Users\rich\app3> dotnet add package SkiaSharp
PS C:\Users\rich\app3> dotnet build
PS C:\Users\rich\app3> Get-ChildItem .\bin\Debug\ -Recurse | Measure-Object -Sum Length | Select-Object Sum

     Sum
     ---
31754654

PS C:\Users\rich\app3> Get-ChildItem .\bin\Debug\*Skia* -Recurse | Select-Object Name, Length, Directory

Name                Length Directory
----                ------ ---------
libSkiaSharp.dylib 8495552 C:\Users\rich\app3\bin\Debug\net6.0\runtimes\osx\native
libSkiaSharp.dll   7177104 C:\Users\rich\app3\bin\Debug\net6.0\runtimes\win-arm64\native
libSkiaSharp.dll   8413072 C:\Users\rich\app3\bin\Debug\net6.0\runtimes\win-x64\native
libSkiaSharp.dll   7081872 C:\Users\rich\app3\bin\Debug\net6.0\runtimes\win-x86\native
SkiaSharp.dll       414096 C:\Users\rich\app3\bin\Debug\net6.0

PS C:\Users\rich\app3> Remove-Item -Recurse bin
PS C:\Users\rich\app3> dotnet build -a x64
PS C:\Users\rich\app3> Get-ChildItem .\bin\Debug\ -Recurse | Measure-Object -Sum Length | Select-Object Sum

    Sum
    ---
8998050

PS C:\Users\rich\app3> Get-ChildItem .\bin\Debug\*Skia* -Recurse | Select-Object Name, Length, Directory

Name              Length Directory
----              ------ ---------
libSkiaSharp.dll 8413072 C:\Users\rich\app3\bin\Debug\net6.0\win-x64
SkiaSharp.dll     414096 C:\Users\rich\app3\bin\Debug\net6.0\win-x64

This app, with a SkiaSharp dependency, is 31MB by default (with the existing behavior) and ~9MB with the proposed behavior. That's a significant benefit. If you have multiple RID-specific dependencies, then the benefit could be higher.

Related: #23539

Context

.NET Core 1.0 started with the concept of both portable and RID-specific apps. We thought of portable apps as being matched with framework-dependent deployment and RID-specific apps being matched with self-contained deployment. That made good sense at the time. The idea is that portable apps could run on any runtime you paired the app with, and self-contained apps could only run in the specific environment that they were published for. Also, self-contained apps were the only apps with executables. Framework dependent. It wasn't until .NET Core 3.0 that framework dependent apps got native executables by default. We now have a platform that has a diverse set of application offerings, which bias to portable or RID-specific apps being preferred. It's worth re-considering which of the two models should be the default.

Portable apps only make sense if the following characteristics are all true:

  • App is deployed as framework-dependent.
  • App doesn't require a native executable for the desired UX, and can be launched with the dotnet MyApp.dll pattern.
  • Size isn't a primary performance metric.

Client apps don't satisfy those characteristics:

  • Desktop client apps require an executable.
  • Mobile apps are self-contained.
  • Wasm apps are self-contained.

Server apps are mixed:

  • For container apps, RID-specific is best since size is a primary performance metric, and because the app will only ever be launched one way. The flexibility offered by portable apps doesn't apply.
  • Apps that are developed and built on one environment (like Windows x64) and uploaded as binaries to a cloud service and then run in another environment (like Linux Arm64) are the poster-child for portable apps.

There is also the scenario where a dependency may have a security issue (CVE) on one platform and not another. It would be nice if you didn't have to service a Linux binary on Windows (or vice versa) just to satisfy compliance rules.

In looking at the landscape, it becomes obvious that most scenarios benefit significantly from RID-specific deployment (either framework-dependent or self-contained) and that only niche scenarios benefit from portable apps. As a result, we should switch to RID-specific builds as the new SDK default.

Note: It turns out we already did this in very early .NET 7, but it wasn't quite complete:

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-NetSDK untriaged Request triage from a team member labels Jan 21, 2022
@richlander richlander changed the title dotnet build/publish produces RID-specific apps by default [breaking] dotnet build/publish produces RID-specific apps by default Jan 22, 2022
@richlander
Copy link
Member Author

richlander commented Jan 23, 2022

Zooming out a bit, there are three different use cases to consider (using partially made up syntax):

  • dotnet build --portable
  • dotnet build --rid-specific specificRid
  • dotnet build --rid-specific --UseSdkRid

Today, the first is the default, the second is a straightforward option, and the last is effectively not supported/possible (even though I'm arguing that it is both a good option and the best default).

There are two questions:

  • Which of the three should be the default?
  • What's the syntax for the other two?

Since I published this proposal, there has been concern raised that it will break tooling. If tooling works best with portable, then it should opt the app into portable publishing as a default, and offer an opt-out if the developer knows better.

Given my proposal, the most obvious options are:

  • Accept the proposal, and offer some combination of any as the portable RID and --portable as an equivalent convenience argument (or -a any).
  • Reject the proposal and offer an easy way to opt into targeting the SDK RID (-r with no RID is one option or -r implicit).

@imba-tjd
Copy link

Currently if you speficy RID, --self-contained=true is implied.
Does using implicit RID mean --self-contained=true by default and produce large files?
IDK if this is desired. Personally I would like self-contained to be opt-in.

@richlander
Copy link
Member Author

This feature has no effect on that. It only changes whether -r RID (or -a or --os) needs to be specified.

@imba-tjd
Copy link

It's related. Now normal dotnet build equals to dotnet build --self-contained=false, but dotnet build -r win-x64 equals to dotnet build -r win-x64 --self-contained=true. If you add -r by default, it changes the behavior of dotnet build

Though I just tried -r win-x64, it gives a warning that One of '--self-contained' or '--no-self-contained' options are required when '--runtime' is used.

@richlander
Copy link
Member Author

richlander commented Jun 15, 2022

Sorry. I wrote so many specs. I got confused which one I was reply on. I will provide a better answer now. Thanks for your patience.

First, the proposal is to make the dotnet build default be like this:

dotnet build --use-current-runtime --self-contained false

Note: We're not going to make this change in .NET 7 (per dotnet/core#7131).

For most apps, there would be ZERO difference. Most apps don't have RID-specific dependencies.

For apps that have RID-specific dependencies, the effective change is that they would be:

  • Smaller
  • RID-specific and not portable

See this spec. It describes a first step on that journey that is very similar/related: #26031.

On the error, that's another point of this journey. I asked the team to insert that warning to enable us to make a breaking change later. It's a way of saying that -r by itself is no longer safe. It's messy, but seemed (and still seems) like a good idea. The current defaults are bad.

@richlander richlander modified the milestones: 7.0.1xx, 8.0.1xx Jun 25, 2022
@richlander richlander changed the title [breaking] dotnet build/publish produces RID-specific apps by default .NET 8: [breaking] dotnet build/publish produces RID-specific apps by default Jun 25, 2022
@symphonic-navigator
Copy link

I'm against it.

In a microservice environment with IoT (ARM64 in addition to AMD64) and multiple types of developer hardware (Windows PC, Linux PC, MacOS) it's important to be able to build portable apps so build step can be done once in CI/CD only, not redundant per target platform.

In my opinion platform independent should be default, the argument "size" doesn't really count when it comes to containers.

It would be more interesting to be able to have dotnet publish being able to publish "external dependencies only" and .NET applications able to use dependencies from a different directory (so we could build images with the dependencies pre-installed).

Things like that.

@nagilson
Copy link
Member

We won't be doing this. New proposal for something related, though not the breaking change: #29950

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-NetSDK untriaged Request triage from a team member
Projects
None yet
Development

No branches or pull requests

5 participants