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

Support using different toolchain versions in one Nerves project #146

Open
fhunleth opened this issue Dec 4, 2024 · 5 comments
Open

Support using different toolchain versions in one Nerves project #146

fhunleth opened this issue Dec 4, 2024 · 5 comments

Comments

@fhunleth
Copy link
Member

fhunleth commented Dec 4, 2024

Currently each toolchain is specified as a hex dependency in the Nerves system. It's important that the toolchain that compiled the Nerves system is compatible with the one being used to compile NIFs and ports in a Nerves project. For example, it would be bad if a Nerves user got a toolchain update that updated the C runtime libraries without the corresponding Nerves system update. That could result in a new function being called that wasn't in the shared libraries included with the system and would cause code to break at runtime.

The way this is currently solved is by using mix dependencies to ensure toolchain versions match and works well most of the time.

The problem comes when you have a multi-target Nerves project. All Nerves systems in that project need to be updated in unison when the toolchain changes. This is because mix only allows one version of the dependency in a project at a time. In theory, the Nerves project should be fine since the Mix target specs for the dependencies ensure this constraint. However Elixir doesn't have a means for supporting this and it may, in fact, cause more confusion if it were added since dependency resolution already is a hard problem.

One solution is to create a new hex package for every major version of GCC. That way a Nerves system that would work better with GCC 14 doesn't need to wait for a GCC 13 bug fix for a different Nerves system. See #145 for an example that is causing this to happen. This also would help improve the situation where if you have a project that pulls Nerves systems from multiple maintainers, you don't have to wait for all of the maintainers to publish an update before upgrading yourself. This situation also requires improvements to the Nerves system, but decoupling toolchains is needed for it too.

The downside of making a hex package per GCC version is that it really adds a lot of hex packages. This looks like an inelegant solution when you first see it, but it's unclear what would be significantly better. It also looks hard to maintain unless this project gets updated to generating mix toolchain projects via scripts.

@Damirados
Copy link

Damirados commented Dec 5, 2024

IMO this problem is already solved by mix targets

Toolchain major versions currently follow GCC major versions.
Mix can work with single target at the time, and single target should only ever use single toolchain.
If toolchain version in deps {~> 13.x} specified correctly major version change shouldn't happen automatically and dependency resolution will fail if other deps end up depending on different versions.
If there is need too change something in toolchain related to older gcc version it could be a minor version change.

I don't see how having new hex package for every major version changes anything here.
It might actually cause a problem because now dependency resolution will not fail anymore between different versions on the same target.

In theory, the Nerves project should be fine since the Mix target specs for the dependencies ensure this constraint.

Is there anything different in practice?

@fhunleth
Copy link
Member Author

fhunleth commented Dec 5, 2024

@Damirados Could you try doing this in a project? Maybe that will make it more apparent when you get the dependency resolution failure due to one project requiring "~> 13.0" and another requiring "~>14.0" even though they're different mix targets.

@Damirados
Copy link

I have setup example repo
and it does give following error

* Getting custom_rpi3 (https://github.com/Damirados/custom_rpi3.git - gcc14-toolchain)
remote: Enumerating objects: 2716, done.
remote: Counting objects: 100% (2716/2716), done.
remote: Compressing objects: 100% (789/789), done.
remote: Total 2716 (delta 1641), reused 2711 (delta 1636), pack-reused 0 (from 0)
Resolving Hex dependencies...
Resolution completed in 0.026s
Because the lock depends on nerves_system_rpi3 1.29.0 which depends on nerves_toolchain_armv7_nerves_linux_gnueabihf ~> 13.2.0, the lock requires nerves_toolchain_armv7_nerves_linux_gnueabihf ~> 13.2.0.
And because your app depends on the lock, nerves_toolchain_armv7_nerves_linux_gnueabihf ~> 13.2.0 is required.
So, because your app depends on nerves_toolchain_armv7_nerves_linux_gnueabihf ~> 14.2.0, version solving failed.

Currently Mix does not consider target when doing dependency resolution.
It is debatable what should mix do here, IMO target should be considered.

@Damirados
Copy link

Following advice from elixir-lang/elixir#14040 I have made changes to mix.exs Damirados/nerves_mix_target_bug@cc5deb9

It works well for this scenario.

@fhunleth
Copy link
Member Author

This is great! Thank you for working through this.

I would definitely like to get this documented publicly somewhere. It will certainly help people.

On the other hand, I have first hand experience with supporting multiple lockfile issues, so I don't want this to be a common Nerves setup. Practically speaking that means that I wouldn't put it in the Nerves new project generator, and that's one place I'd need it to support a mixed GCC 13/14 set of systems.

The alternative of creating a toolchain package with the gcc version number in it is looking really interesting to me. I know it's ugly, but it's conceptually simple and would work with zero effort from Nerves users. The total lack of end user support that we'd need to do is what's most appealing. The decoupling of release timing between systems maintainers is also appealing and will allow Nerves to scale to more platforms as it will be easier to include deps for less-frequently maintained hardware. I'm assuming a nerves_system_br change as well, so it would be fair to explore that that change definitely is possible before committing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants