In the default build.zig.zon file you got these lines regarding the package version:
// This is a [Semantic Version](https://semver.org/).
// In a future version of Zig it will be used for package deduplication.
.version = "0.0.0",
I think it’s strange that the package manager would force developers to follow a particular versioning scheme. To me it doesn’t matter if you use semver, something looking like semver, calver, or some other fancy scheme the developer wants to use. So I’m wondering what the devs are thinking regarding the version scheme, is it just a coincidence that semver is the only supported option or is it a deliberate choice that is likely to stay that way.
This is deliberate and will almost certainly remain. This is currently vaporware, but in the future, the compiler will be able to detect when multiple versions of one package exist in a compilation (typically through transitive dependencies, e.g. you depend on A and B, and A depends on v1.0.0 of C, while B depends on v1.1.0 of C). The key thing is that it can then use Semantic Versioning to figure out what it should do; if the major version is different a compile error must be raised since the APIs will conflict, but if only the minor/patch differ then the compiler can choose to replace the older (lower version) dependency with the new one.
@mlugg I can see the theoretical benefits of semver, but they require the developers to be disciplined, and sometimes they have to ask fairly difficult questions like if a bugfix, which changes behaviour, is just a bugfix or if it requires a major bump because someone relied on that particular behaviour.
I disagree that the compiler should check for compatible versions. Just because package C updated to v2.0, it doesn’t mean that all APIs have changed. If I try to compile a project using packages A and B and the compilation failed because the metadata of package C changed while nothing I’m using changed, I would think that is a compiler bug. If the compiler is not smart enough to figure out what APIs have changed between versions and check if the ones I’m using are affected, it should not do this check at all IMO. [1]
As a corollary, since we cannot be sure what APIs have changed between version bumps, I think any X.Y.Z…-alfalfa versioning scheme is as good as semver, and especially so if the developers don’t fully follow the semver standard.
I do understand the goal is to make it easier for the developer to find possibly hard-to-find bugs (which is great!), but as you can tell I think it will have too many false positives.
@dimdin I know that semver is a popular format but I still think forcing people to use it the wrong way to about this as I explained above.
You can probably tell I also dislike packages upper-bounding the versions of dependencies. They cannot know what APIs will change so even there it’s best to just lower bound it. ↩︎
Here’s what’s accepted. It’s slightly different than what is in the original proposal above:
Always MVS. This means that the package manager will only fetch dependencies that are explicitly listed (with their checksums), but it will pick the maximum version among those options.
Packages with versions >= 1.0.0 may not depend on packages < 1.0.0.
Incompatible major versions between the same packages within a dependency tree will be an error by the package manager, or it will just pick the latest version, even if it is incompatible, and rely on the package author to make any breaking changes compilation errors rather than runtime bugs.
“MVS” refers to Go’s Minimal Version Selection, described in this article. For this algorithm to work out in practice, it requires the use of a semver-like versioning scheme where minor version bumps are backward compatible with older versions of the same package. If a package makes a backward-incompatible change to its public API, it must increment its major version. If a package introduces a breaking change without bumping the major version, it should be considered a bug just like any regular bug introduced by incorrectly written code.
Don’t put too much aesthetic value into versioning. It is okay for the “informational” version (used for promotion/marketing) of your project to be “v2” or “2024-12” while its package version is 7.3.26 (due to it’s development history and breaking changes).
It would be relatively straightforward to add a date as an additional field:
.released = "2024-11-11",
I don’t think Zig should have two ways to write a version, let alone more than that. SemVer isn’t perfect, and it isn’t a law of nature, but the advantages of having one concept of versioning across everything outweighs any minor shortcomings it has.
My issue is still that semver just doesn’t make sense to me on the package level. I think it makes sense for specific public interfaces, like function fooBar is version 2.0.0 which introduces a breaking change compared to fooBar 1.3.7. I do realize that it is functionally impossible to version all public APIs, but that is the only place a strictly enforced Semver system makes sense to me.
It’s not about aesthetic values, I don’t give two hoots what the versioning looks like for other’s packages, and I expect the same of the package manager. I also don’t want artificial breakage because something in my dependency chain was updated but the thing in that package that I depend on wasn’t. It’s not a problem right now, maybe it won’t be a problem at all, but my experience in the Python ecosystem packages who set upper bounds tend to be the worst to deal with.
Semver is just as good as any other versioning system, it’s just a tag where bigger number means newer. My problem is when it turns into an enforcement.