RFC: Name the first release from a branch N.1.0 instead of N.0.0

Concretely, I propose that we change the process for cutting a release branch to have an extra step:

  • In the release branch, bump the version to X.1.0git (where X is the current major version in the branch.)

The purpose of this would be to better-distinguish actual release-branch builds from development branch builds.

Currently, LLVM’s main branch calls itself “18.0.0”. This version number is in place for all 6 months of development, starting immediately after the LLVM 17 branch was cut. Then, the first actual release from the release branch will also be called “18.0.0”. There is no easy way to distinguish between the two – other than the “git” suffix, which is non-obvious for humans, and not available via preprocessor comparison, e.g. via __clang_major__ __clang_minor__ __clang_patchlevel__.

This inability to distinguish is exacerbated via downstream projects which take LLVM at an arbitrary mainline git revision, and release a toolchain based on that. They might be distributing an “LLVM 17” toolchain, which is actually from a git snapshot in January (right after the LLVM 16 branch-cut!), yet, which looks the same as an actual “LLVM 17” release. (Now, of course, it’s not possible to solve all problems arising from use of such mainline snapshots, but I think this proposal is an easy way to at least make it slightly clearer what you are actually using.)

This proposal is also quite similar to the way GCC numbers its releases, although GCC also increments the minor version for each subsequent patch release. I do not propose to change LLVM’s current policy on patch release numbering, so each such patch would continue to increment only the third digit. Thus, LLVM 18’s releases would be numbered “18.1.0”, “18.1.1”, “18.1.2”, …

7 Likes

Since we rarely use the minor version anyways I’m fine with this as long as we don’t start with N.2.whatever at some point. That would make things very confusing.

This approach seems inherently risky regardless of release numbering scheme. Are we trying to make it clearer to the project maintainers, or the users?

I’m not trying to change which revisions downstream projects choose to distribute, only trying to make it possible for users to easily distinguish a release branch from mainline.

I think it’s reasonable to be able to distinguish “derived from main” and “derived from a release branch” this way. We don’t use that minor version for anything else.

We ship Clang continuously (usually on a weekly basis) and we regularly run into an issue where our users would report a bug like “we have updated Fuchsia Clang toolchain from N-1 to N and now we’re seeing an error …” but this is difficult to triage because N here could be any of the roughly 20 releases we would do in the 6 month period when LLVM main branch calls itself N.

I think this proposal is an improvement as it would allow distinguishing development versions from the release ones, but it doesn’t address the issue I described above (we would never use N.1.X because we follow mainline).

The alternative solution I had in mind is to introduce a new CMake option to omit the version altogether. When this option is enabled, binaries would simply be named clang and libLLVM.so (instead of clang-N or libLLVM-N.so), and the Clang resource directory would be located at lib/clang. LLVM tools would also omit the version from the --version output, only printing the Git revision which is the information we’re really looking for in bug reports. If we had such an option, it could be enabled by default in the main branch but disabled in the release branches.

There was 11.1 because 11.0 broke the ABI somewhere unintentionally, so unless we can rule out such a scenario from every happening again, N.2 could happen if we start at N.1.

IMO this RFC needs an answer to:

  • how could an ABI break discovered after N.1.0 be fixed (without requiring {N+1}.1)?
  • how would that translate to SOVERSION numbers for the affected libraries?

For 11.0, the SOVERSION changed from 1111.1, but presumably people would want to keep SOVERSION = N also in the new scheme, meaning that we’d have to go from SOVERSION = N for N.1 to SOVERSION = N.2(?) for N.2. That’s annoyingly inconsistent, but perhaps acceptable given how rare such unintentional ABI breaks should be.

I’m not sure what the concern is. We would use the version number 18.2.0 if there had to be an ABI break within the release/18.x branch after 18.1.0 was already released. And we’d use 18.394.77 if we made 393 patch releases with ABI breaks, followed by 77 non-ABI-breaking patch releases from the 18.x branch. (Highly unlikely, of course.)

As I said: I propose not to change existing policy for patch releases. Any situation where we would currently increment the “minor” version number on a release branch, we should continue to do so. It’s only happened once so far, but it could happen again.

I don’t propose to change how SOVERSION is produced from how it’s done now. The current code would produce an SOVERSION of 18 for a release with version number of 18.1.0.

When we made the 11.1 release, we had to specifically modify the SOVERSION code to include both major and minor version numbers – but we only did it on the 11.x branch. Potentially it would’ve made sense to make that change on the mainline too, but I’m not proposing to change that situation via this RFC.

I was responding to a statement that said “please don’t do N.2”, and trying to square that with how the RFC would work in case we need to bump the minor version.

As someone who triaged hundreds of decade-old crashes, I’d definitely appreciate additional indicator whether crash report comes from development version. Because this might be the reason why I’m not able to reproduce it using period-correct release versions.

Apple’s toolchain would still fabricate lies for its version and associated macros, so the version information out of Clang would still be unreliable.

Why not change the development branch instead?
We could use for example 999 as unique number for the dev branch:

18.999.0 (next one will be 19.999.0 and then 20.999.0).

Or even: 18.999.999, or anything that will unambiguously raise attention that this is a dev branch and not a release branch.

1 Like

Do you mean that after the official release of N.0.0, the main branch would become N.999.0, then followed by N+1.0.0 release? Mathematically that makes sense to me.

1 Like

From my experience, AppleClang crash reports are both rather rare and easily identifiable via other means (e.g. triple). I agree that anything we convey through versions is not going to work for those crash reports, but it doesn’t undermine motivation I provided.

Yes, that would also work, but I think the original proposal is preferable:

GCC set a version-numbering-policy precedent, and absent strong reasons to be different, I think it’ll be simpler for users to have a similar policy.

The tag llvmorg-18-init would end up with version number 17.999, which seems confusing (or we’d have to change how that tagging works too).

Also, as a more minor issue, IMO 18.999 (which is on mainline) looks like it should be a version derived from 18.X (release-branch), but it’s actually derived from 17.999 (mainline). In the original proposal, the number continuity works out better: the second/third numbers always show releases off a release branch, derived from the mainline with the same initial number.

Is it possible to set __clang_minor__ to x for the developing version indicate that it is not ready? Or adding a new suffix .git to the version string so that it will be less confusing.

Since I think the key point here is that LLVM’s main branch calls itself “18.0.0”. This is the confusing part so that we should try to improve this directly.

Thanks for raising this, James! A good example of the problem was when SQLite hit a bug in LLVM, and it wasn’t clear at all that the “clang 11.0.0” version used was a pre-release build (the only way to notice was really to know that 11 hadn’t been released yet).

I think the most unfortunate part of the problem is that Clang doesn’t print the LLVM_VERSION_SUFFIX. For example, my local vanilla cmake build of clang identifies as

clang version 18.0.0 (https://github.com/llvm/llvm-project.git 9a99a1a39e1d067abb9a6cc0d53e7708d6c49995)

If that said

clang version 18.0.0-PRERELEASE (https://github.com/llvm/llvm-project.git 9a99a1a39e1d067abb9a6cc0d53e7708d6c49995)

instead, I think that would go a long way towards fixing the confusion.

I’m not opposed to the proposal of bumping the minor version on branch cut, but that doesn’t really solve the problem of distinguishing between a release and non-release version, only between a release branch and trunk.

3 Likes

I think if we implement this proposal, we should also add the minor version to the SOVERSION. N.0.0 (trunk) would have a different ABI than N.1.0 (release branch).

What is the confusion if it lists the commit SHA it was based on? That’s enough to identify whether it was built from dev/release or some other custom branch.

Not sure why we do that…but just sent a PR to fix it. (I do not consider that as a replacement for this RFC proposal.)

It’s also the case today: 17.0.0 (mainline) has a different ABI than 17.0.0 (release branch). Of course, 17.0.0 (mainline) actually had many different ABIs…In any case, the situation isn’t really changed by this proposal.

But, yes, I think it would be fine to adjust the SOVERSION scheme to use major+minor. I’m not really sure why we didn’t do that already, back when we had to modify it for the 11.x branch.