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

Microsoft.NETCore.App 1.0.1 app does not run on .NET Core 1.1.0 #2542

Closed
tmds opened this issue Nov 22, 2016 · 18 comments
Closed

Microsoft.NETCore.App 1.0.1 app does not run on .NET Core 1.1.0 #2542

tmds opened this issue Nov 22, 2016 · 18 comments
Assignees

Comments

@tmds
Copy link
Member

tmds commented Nov 22, 2016

$ dotnet run
Project hwapp (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation.
The specified framework 'Microsoft.NETCore.App', version '1.0.1' was not found.
  - Check application dependencies and target a framework version installed at:
      /opt/dotnet/shared/Microsoft.NETCore.App
  - The following versions are installed:
      1.1.0
  - Alternatively, install the framework version '1.0.1'.

As a developer, I want to build my app once and have it working on as many distributions possible for as long as possible without having to do anything myself :)

Currently an app for A.B.C will run on A.B.D (D>=C). It would be nicer if it were also running on A.E.F (E>B) if no A.B.D was found.

This will be very annoying when you upgrade your distro and all your compiled apps no longer run because the new version of the distro includes a new "current release" of .NET.

@gkhanna79
Copy link
Member

@tmds In general, our policy is to keep patch version updates rolling forward as they maintain strict compatibility requirement of being functional equivalent with their predecessors.

When incrementing minor version, we may not be bug-to-bug compatible while still being functionally compatbility. Hence, rolling forward is not necessarily going to work and thus, you see the behaviour above.

To target new minor (or major) versions, one will need to consume it using the required tools. @leecow can share more details on this policy and also point to the tools that help buiding apps for 1.1.0.

@tmds
Copy link
Member Author

tmds commented Nov 22, 2016

Can you clarify what you mean by bug-to-bug compatible? And how this is different between patch and minor increments?

@leecow
Copy link
Member

leecow commented Nov 22, 2016

@tmds @gkhanna79 The only time updated tooling would be needed to target an particular shared framework is if specific functionality were added to the tooling. An example might be additional Linux distro support that did not previously exist.

@tmds Our LTS releases are meant to satisfy exactly your long-running scenario. Careful attention is paid to patch compatibility so there can be confidence that your application will continue to work as expected across the entire range of A.B.x updates. Since Minor version updates (what we are calling 'Current') don't have such a strict compatibility requirement, it was decided to not automatically update across this boundary.

I believe what Gaurav is referring to with bug-to-bug compatibility is that when an issue is fixed which will target a patch version (eg 1.0.3), the functionality being fixed is also needed in the next 'Current' branch. In some cases, the safest fix to maintain compatibility for a patch version may not be the 'best' fix from a long term engineering perspective. In these cases the fixed functionality may be arrived at in different ways which can lead to behavioral changes. These changes may be acceptable across minor version boundaries but violate the compatibility contract for a path release.

@tmds
Copy link
Member Author

tmds commented Nov 22, 2016

The LTS releases satisfy the long-running scenario but not the ability to widely deploy. New versions of a distro supported by a new current release won't run your app since new the support is not back-ported to the LTS release. E.g. if your app targets 1.0, it doesn't run on fedora 24 which is now supported by 1.1. So if you want to support these new versions, you need to re-target your app on each current release. Similar when the new distro version drops support for a current release, you have to make sure your app targets one that is still supported.

With the current scheme I find it hard to determine what versions a developer should target and what versions a distro should maintain.

@tmds
Copy link
Member Author

tmds commented Nov 23, 2016

\cc @DamianEdwards

@tmds
Copy link
Member Author

tmds commented Dec 1, 2016

Picking this up after a week.

This issue is about this 1 line in core/support that says:

Applications do not automatically begin using the new minor update.

Because .NET Core versioning uses SemVer, a change of minor guarantees the API to be backwards compatible.

I understand that changing a minor may have differences in behavior (e.g. a bugfix on 1.2 which is not in 1.1). And an application may have relied on this behavior causing it to break. So I think the preferable thing to do is to use a minor which matches that of the application. By not automatically picking up higher minor when the matching is not available, both developers and distro maintainers get stuck in a "Write once, Build everywhere".

Before netstandard, library developers had to target multiple platforms. Now they get a common API, which may have it's quirks on the different implementations. Similar, it should be possible to target the .NET Core API and accept the quirks that may arise from one minor change to the next.

Considering the current approach, what would be your advice to Alice and Bob in a year or so, assuming there are 4 versions of .NET Core: 1.0, 1.1, 1.2 and 2.0.

  • Alice is maintaining the .NET Core package for the Banana linux distro. Which version(s) of .NET Core should she include for the next release of Banana linux?
  • Bob wrote an awesome .NET Core console app. He'd like to release it on his GitHub project to allow as many people as possible to run it. What version(s) of .NET Core should he target?

@RheaAyase
Copy link
Member

That is already a question i'm about to ask in our next meeting...
Regards,
"Alice"

@terrajobst
Copy link
Contributor

[@tmds] Similar, it should be possible to target the .NET Core API and accept the quirks that may arise from one minor change to the next.

That sounds great in principle, but it doesn't work so well in practice. The .NET Framework has exactly the characteristic you describe and as a result, the degrees of freedom we have with adding new features to it are severely limited.

Keep in mind that compatibility isn't a black & white thing: even changes most devs would describe as compatible have resulted in applications not being able to run on the later version of a framework. Let me quote from my blog post:

Unfortunately, we’ve also learned that even compatible changes can break applications. Let me provide a few examples:

  • Adding an interface to an existing type can break applications because it might interfere with how the type is being serialized.
  • Adding an overload to a method that previously didn’t had any overloads can break reflection consumers that never handled finding more than one method.
  • Renaming an internal type can break applications if the type name was surfaced via a ToString() method.

That's why for .NET Core we want to roll patch changes forward (third digit) while feature additions (major or minor) aren't.

@tmds
Copy link
Member Author

tmds commented Dec 2, 2016

@terrajobst Totally agree. It's preferable to use the exact minor to avoid these issues.

What's your advice for Alice and Bob?

@terrajobst
Copy link
Contributor

My advice for Bob is simple: create a self-contained console app. We're also planning in investing more into linker tools so that the app can be tree shaken. In other words, Bob wouldn't run on a shared framework. This allows Bob to use whatever he wants with no external dependencies (besides the OS, of course).

For Alice, that's a bit more nuanced. Generally speaking, I'd expect the distros to select the .NET Core versions based on support policies (LTS vs. fast track).

@tmds
Copy link
Member Author

tmds commented Dec 6, 2016

My advice for Bob is simple: create a self-contained console app. We're also planning in investing more into linker tools so that the app can be tree shaken. In other words, Bob wouldn't run on a shared framework. This allows Bob to use whatever he wants with no external dependencies (besides the OS, of course).

As far as I understand, this involves Bob to publish against each RID. Which means as distros create new releases, Bob will have to target those as well otherwise his app won't run there. He will also need to package his app for each RID. Since the RIDs he targets have a .NET runtime, he's better off depending on the shared framework and providing a single download.
To have an advantage by publishing his app, Bob should be able to target the linux-x64 RID, which doesn't work atm.

For Alice, that's a bit more nuanced. Generally speaking, I'd expect the distros to select the .NET Core versions based on support policies (LTS vs. fast track).

If you look at Windows of RHEL, it's feasible to build each .NET Core version because your target runtime is not changing often. When support for a certain version has stopped, apps will still run there. If your distro is moving fast, like Fedora or Ubuntu: not building a version of .NET Core (e.g. because no longer supported), means apps no longer run there (because apps don't automatically use the new minor).

@terrajobst Let me know if I've missed something. I will close the issue afterwards.

@tmds
Copy link
Member Author

tmds commented Dec 13, 2016

@terrajobst Is there still something you want to add? Should I close the issue?

@terrajobst
Copy link
Contributor

terrajobst commented Dec 13, 2016

As far as I understand, this involves Bob to publish against each RID. Which means as distros create new releases, Bob will have to target those as well otherwise his app won't run there.

That' correct.

He will also need to package his app for each RID.

The packaging for self-contained apps is either XCOPY or OS-specific installer/package manager. Sharing vs. non sharing doesn't change that. What does change is the number of outputs for your app.

Since the RIDs he targets have a .NET runtime, he's better off depending on the shared framework and providing a single download.

Whether Bob is better off depends on the goals Bob has: does he care about control of his dependencies? Does he care about being able to use the latest bits? Would Bob prefer an AOT tool chain?

Sharing isn't net wise better; it comes with a separate set of trade-offs.

If your distro is moving fast, like Fedora or Ubuntu: not building a version of .NET Core (e.g. because no longer supported), means apps no longer run there (because apps don't automatically use the new minor).

That's fair, but I'm not sure automatically rolling forward is making Alice's life easier or harder. If the distro you're targeting moves fast, then maybe Alice would be better of building the app specifically for that distro, i.e. going down a self-contained path.

The assumption you're making is that rolling Alice forward will result in her app to continue to run on the higher version of the shared framework. In my experience, while that's often true, isn't a guarantee.

That being said, I don't think there is anything fundamentally wrong with giving the app-author the option to mark their app as "roll forward to the smallest shared framework that my app is targeting". Personally, I'd even be in favor of making this the default with the understanding that this cannot, in principle, be guaranteed to work. The app might depend on bugs in the framework that fixed in the higher version.

@tmds
Copy link
Member Author

tmds commented Dec 14, 2016

That being said, I don't think there is anything fundamentally wrong with giving the app-author the option to mark their app as "roll forward to the smallest shared framework that my app is targeting". Personally, I'd even be in favor of making this the default with the understanding that this cannot, in principle, be guaranteed to work. The app might depend on bugs in the framework that fixed in the higher version.

If by this you mean "use a higher minor if the exact is not available": 👍 👍 👍

By doing this, Alice can adopt the strategy of supporting LTS releases and the latest minor release (for each LTS). If Bob wants to minimize the chance of breaking due to automatic minor upgrades, he'll target an LTS release.

@tmds
Copy link
Member Author

tmds commented Dec 21, 2016

@terrajobst the tools are not following the rules...

netcoreapp1.0 tools run on 1.1.
and prefercliruntime makes it a feature: dotnet/cli#4814.

@tmds
Copy link
Member Author

tmds commented Jan 11, 2017

To have an advantage by publishing his app, Bob should be able to target the linux-x64 RID, which doesn't work atm.

Looks like this is being worked on in the coreclr/corefx repos. 👍

@gkhanna79
Copy link
Member

Is there anything outstanding on this issue?

@RheaAyase
Copy link
Member

With recent changes and linux-x64 rid, Bob will sure be happy. Not that much for Alice, however, this will in a way solve her problem as well - because Bob will be able to simply target linux-x64 and publish with all the things included, if they can't keep up with dotnet minor versions. They won't really need any kind of framework installed on the target machine, therefor Alice is not fully responsible for providing it to Bob and can simply go with LTS and latest.

@tmds tmds closed this as completed Jan 24, 2017
@msftgits msftgits transferred this issue from dotnet/core-setup Jan 30, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 27, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants