In a recent discussion of Zig, some new users complained about broken example code. They encountered these problems because they installed the latest tagged release of Zig (probably from their system package manager) rather than the latest development build. As the Zig ecosystem grows, this is likely to be an ongoing pain point. It will help if authors of published Zig code (outside the Zig project) have an easy way to inspect the Zig version at comptime.
@hasDecl is already a great way to perform compile-time "feature detection". However, it may not be enough if one wants to determine whether or not a file with new syntax can be imported, or if it's a version of Zig that shipped with a known bug that needs to be worked around. I propose the following changes:
zig version reports itself as 0.6.0+bd89bd6fd. For semantic versioning compliance, it ought to be something like 0.7.0-pre+bd89bd6fd. A fancier implementation could include commit height from the most recent tag, e.g. 0.7.0-pre.123+bd89bd6fd. Implemented by #6509.std.builtin.Version type to support pre-release labels according to the Semantic Versioning spec. Implemented by #6566.std.builtin.version).Julia had a similar convention during its wild west pre-1.0 days. I maintained a package during that time, and retaining compatibility with multiple toolchain versions was helpful for end-users.
The issues brought up in that discussion are due to changes in the standard library rather than ones in zig syntax (though I don't doubt people are hitting those too). IMO this goes into a discussion of how to depend on libraries; particularly ones with specific versions.
For semantic versioning compliance, it ought to be something like
0.7.0-pre+bd89bd6fd
I don't like that it forces you to choose the next version ahead of time. In many of my nodejs packages, I wouldn't know what version was next, maybe it would be a bug fix release, maybe a minor release, or maybe I would break compatibility. Right after you tag a release, you have no idea what the next one is going to be.
I suppose one could do the bump immediately upon making such a change. So if you do a bug fix commit, the version would be x.x.1-pre-foo and then if you do a commit it becomes x.1.0-pre-bar and then if you do a breaking change it becomes 2.0.0-pre-baz. So you would end up with these pseudo version strings of releases that never happened. That seems like it might confuse people.
@andrewrk i've seen that some projects use 'x.x.999' for development builds
I don't like that it forces you to choose the next version ahead of time.
Agreed. I dislike the friction between semantic versioning and just doing something simple like git describe. Should Zig switch to that instead? For the purposes of this proposal, the version format isn't actually important. There just needs to be a way to determine if a build of Zig is older or newer than a predefined version.
i've seen that some projects use 'x.x.999' for development builds
projects like glibc use is 9000 (which has caused trouble with Zig #5082) but that forces a maximum amount of patch versions though that shouldn't introduce any problems but you never know
I don't like that it forces you to choose the next version ahead of time.
It turns out the SemVer repo has a long-standing issue with vigorous debate on this topic: https://github.com/semver/semver/issues/200
There was one proposal I liked, which is changing + to affect ordering and denote unstable post-release builds. (Build metadata would then be denoted by another character such as ~). However, it's highly unlikely this idea will be adopted because it would require SemVer 3.
For the SemVer 2 status quo, I think this comment is the best takeaway:
The next nightly should just be a prerelease. If you don't know what version it'll be, make it a patch prerelease. once you know it's a minor or a major, make it a prerelease of that.
For pre-1.0 Zig, I think we should just do 0.(n+1).0-dev and call it day. Sticking with SemVer for this proposal means that any version structure and parsing code will remain applicable for the future package manager.
For what it worth, this may interest you: https://csemver.org/
I feel compelled to strongly emphasize that nightly/development versioning is to SemVer like a square peg is to a round hole.
SamVer communicates a few specific bits of guarantee about the public interface. When you don't have that to say, all SemVer can do is explicitly say that you are not saying it (this is what 0.* and pre-release versions in SemVer say).
I strongly encourage considering the idea of doing SemVer for official releases and not-SemVer for things like nightlies or other non-release builds where you don't actually know what the corresponding release version or public interface changes will be.
Development builds are just pre-releases, which is something that SemVer already supports.
@jayschwa BNF of SemVer implies that things like build metadata go into metadata which is not part of a version at all.
As for pre-release's, its a matter of opinion, and my opinion is that current 0.6.0 is nothing like what 0.7.0 will be, and first commit post 0.7.0 is even further from 0.8.0, calling it a pre-release is an oxymoron at best.
@jayschwa BNF of SemVer implies that things like build _metadata_ go into metadata which is not part of a version at all.
As for pre-release's, its a matter of opinion, and my opinion is that current
0.6.0is nothing like what0.7.0will be, and first commit post0.7.0is even further from0.8.0, calling it a pre-release is an oxymoron at best.
Things that are critical to this proposal:
Constraints of SemVer (what Zig currently uses):
PR #6509 works within these constraints. It twiddles a couple characters in git describe output to make it SemVer-compliant. The resulting version string isn't going to win a beauty pageant, but it will get the job done.
Well 0.x.x is not really a semantic versioning, only chronological. Zig's 0.x.x releases are more like 1.0.0-pre.x, since they are trying to be semantic. It could look like this
X.d.number-of-commits+metadata (d for dev)X.f (f for final, can be anything after d)1.0.0-pre.7.d.88+b0c5d7 < 1.0.0-pre.7.d.122+abc123 < 1.0.0-pre.7.f < 1.0.0-pre.8.d.56+678dcf < 1.0.0
Or simulate post releases:
1.0.0-pre.6.next.88+b0c5d7 < 1.0.0-pre.6.next.122+abc123 < 1.0.0-pre.7 < 1.0.0-pre.7.next.56+678dcf < 1.0.0
So I agree that you're doing the right thing assuming those constraints, but my point here is that even if you are using SemVer and want "the ability to compare Zig builds (including untagged builds)" you don't actually have to limit yourself to just the semantics of SemVer.
I can't tell if you're making a common intuition mistake here, so I'll just make it explicit: Just because SemVer treats build metadata as irrelevant to SemVer comparison, does not mean SemVer disallows using build metadata for your comparisons in addition to SemVer. At least I see no reason why this should be the interpretation. When I read the SemVer spec, I don't see any implication that additional semantics beyond SemVer are disallowed - only what interpretations/usages are disallowed within SemVer semantics.
So what I'm saying is, if Zig uses build metadata for development releases, and provides some function in its standard library which given a Zig version number says "this is a development build" (and sorts accordingly) based on the build metadata tag rather than on the pre-release tag, then to my language/spec "lawyer" intuitions, this does not feel like a violation of SemVer, but rather a composition/layering of SemVer semantics with additional semantics.
Most helpful comment
Agreed. I dislike the friction between semantic versioning and just doing something simple like
git describe. Should Zig switch to that instead? For the purposes of this proposal, the version format isn't actually important. There just needs to be a way to determine if a build of Zig is older or newer than a predefined version.