Skip to content

std: replace builtin.Version with SemanticVersion#13998

Merged
andrewrk merged 1 commit intomasterfrom
unknown repository
Jun 17, 2023
Merged

std: replace builtin.Version with SemanticVersion#13998
andrewrk merged 1 commit intomasterfrom
unknown repository

Conversation

@ghost
Copy link

@ghost ghost commented Dec 18, 2022

This removes std.builtin.Version in favor of std.SemanticVersion.

I don't think this is something that belongs in std.builtin.

  • This avoids the confusion when you don't know which of the two structs to use.
  • The patch field must now always explicitly be specified and no longer defaults to 0.
  • With this upgrade, some strings previously accepted by std.builtin.Version are no longer accepted by std.SemanticVersion, and vice versa. There aren't a lot though. std.SemanticVersion is probably more correct.
  • I think the name is good as-is and coincidentally SemanticVersion is the same length as builtin.Version.
  • SemanticVersion gives you less errors than builtin.Version does on parse.
  • I don't expect there to be too much code breakage because the two are still quite compatible.

I took over the old tests from std.builtin.Version and added them in lib/std/SemanticVersion.zig (basically the only valuable things).

Fixes #12862

@ghost
Copy link
Author

ghost commented Dec 19, 2022

Ok so there's this problem where we have versions that we want to parse but they don't conform to SemVer

\\ <string>11.1</string>
\\ <key>ProductVersion</key>
\\ <string>11.1</string>
\\ <key>iOSSupportVersion</key>
\\ <string>14.3</string>

Semantic Versioning does not accept "1.0" for example. The patch part must be specified.

Edit: so I decided to implement version parsing specifically for the cases there, which wasn't very complicated at all.

@ghost
Copy link
Author

ghost commented Dec 22, 2022

#12862 (comment)

@@ -1048,7 +1048,7 @@ fn buildOutputType(
try cssan.addIncludePath(.iframework, arg, args_iter.nextOrFatal(), false);
} else if (mem.eql(u8, arg, "--version")) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs for this is "Dynamic library semver" which wasn't actually true. Now it is because it no longer accepts an omitted patch component (according to the spec "1.0" is invalid). This might cause breakage.

const major = version_components.first();
if (major.len == 0) return error.InvalidVersion;
const minor = version_components.next() orelse return error.InvalidVersion;
const patch = version_components.next() orelse "0";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This version doesn't check that there aren't additional components after the patch. Same error is repeated in zig/system/darwin/macos.zig, would it instead be better to provide a properly tested non-standard function somewhere so that other people don't repeat it by implementing their own version?

Copy link
Author

@ghost ghost Jan 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above brings me to this comment: where shall we put this function and its tests? It should probably be an entirely internal function. lib/std/zig/CrossTarget.zig and lib/std/zig/system/darwin/macos.zig share lib/std/zig/ so should it be lib/std/zig/version.zig or something?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd either put it in SemanticVersion.zig and clearly document it as non-standard or just lib/std/zig.zig.

Copy link
Author

@ghost ghost Jan 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I was just doing this but now I'm thinking is std.zig really the right space for this? Version parsing like that isn't really part of parsing Zig code. Doesn't really seem to fit in that file.

Well, I was gonna say another motivation of this PR was to reduce the public API surface a bit, and really, after this missing check at the end it seems to be complete now. The code really isn't that complicated.
The advantage is that now we have a single, standard way to parse a version.
For all other cases the user has to write this themselves, and it really isn't that complicated, and also only an optional patch component seems kind of specific. What if the user wants more components to be optional, etc.?
So this way it's also very formidable for the use case at hand.

So for now I went back to the way it was and fixed up the parsing in both places.
However if you can think of a better place, where ideally it doesn't get into the public std, maybe we can do it the way your proposing.

Maybe lib/std/zig/version.zig? Can we put it there publicly and then only use it in those two places and not expose it to the public std API surface?

And if we put it in std.SemanticVersion as non-standard, I think we should make it customizable and allow the minor component to be optional too, at which point maybe it gets a bit too complicated? I don't know. Maybe it's ok then.

@andrewrk
Copy link
Member

Looks pretty good. Would you mind rebasing on latest master branch?

@ghost
Copy link
Author

ghost commented Feb 21, 2023

Going to wait for a new master build first.

@ghost
Copy link
Author

ghost commented Feb 22, 2023

Rebased.

@ghost
Copy link
Author

ghost commented Feb 22, 2023

stage1/zig.h has a lot of changes. All I did was zig build update-zig1 --zig-lib-dir lib. So, most changes are coming from the second commit, but I guess it's intended?

@kcbanner
Copy link
Contributor

#14691 added a step that copies zig.h into stage1/ but maybe that wasn't run as part of that PR?

@ghost
Copy link
Author

ghost commented Feb 22, 2023

That could very well be it. Maybe it's ok to do it here. I might split it into 3 commits at least, though.
Also, I can see a CI failure, which looks weird. I should probably investigate that first.

@ghost
Copy link
Author

ghost commented Feb 24, 2023

Not yet sure what's up here. Running zig build test --zig-lib-dir lib gives me an enormous amount of memory leak logs bigger than my terminal buffer size. I seem to be hitting some kind of a compiler bug?
I wonder if this is in fact related to those stage1/zig.h changes. In the future someone else will run zig build update-zig1 and then if they have the same issue, that's probably it.

@Vexu
Copy link
Member

Vexu commented Feb 24, 2023

Running zig build test --zig-lib-dir lib gives me an enormous amount of memory leak

Known issue, build with llvm backend enabled so that the compiler uses malloc.

@ghost
Copy link
Author

ghost commented Feb 26, 2023

So I ran the zig build test-std tests locally and they all passed so I suspect this is an issue outside the std and possibly caused by the stage1/zig.h changes or something else. It's pretty hard to debug the compiler bug at this scale so for now I could just wait and see if the issue resolves itself sometime after some rebases.

@ghost
Copy link
Author

ghost commented Mar 4, 2023

Another PR of mine #14782 is hit with the exact same CI failure. Can anyone tell me the correct way to rebuild stage1/? I don't seem to be doing it correctly.
Am I supposed to run zig build update-zig1 with or without --zig-lib-dir lib and am I supposed to use a Zig compiler built by following the steps https://github.com/ziglang/zig/wiki/Building-Zig-From-Source#option-a-use-your-system-installed-build-tools in this branch or can I also use a Zig compiler downloaded from https://ziglang.org/download?
Anyway, should work just fine when I simply don't update stage1/zig1.wasm...

@ghost
Copy link
Author

ghost commented Mar 4, 2023

So this has some interesting build-related breaking changes:

const std = @import("std");

pub fn build(b: *std.Build) void {
    const optimize = b.standardOptimizeOption(.{});
    const target: std.zig.CrossTarget = .{ .os_tag = .macos };

    const lib = b.addSharedLibrary(.{
        .name = "a",
        .version = .{ .major = 1, .minor = 0 },
        .optimize = optimize,
        .target = target,
    });

The patch version component must be specified:

        .version = .{ .major = 1, .minor = 0, patch = 0 },

Here's a shorter more nicer-looking alternative:

        .version = std.SemanticVersion.parse("1.0.0"),

(shortness depends on what you have imported as well)

In the future this might become shorter and more nicer when we have anonymous function calls like this:

        .version = .parse("1.0.0"),

(don't remember the proposal for this)
I think that'll be pretty nice.
And it's a build script so it doesn't matter whether it parses that string at comptime or at runtime, anyway.


So, packages previously used std.builtin.Version and now std.SemanticVersion, but that makes me think--don't we force a certain way of doing versioning on all users now? What if they don't follow semver? Isn't this a bit wrong?

I am thinking if maybe std.SemanticVersion should be std.Version and people that wish to interpret it as semver can do so and everyone else can interpret it as simply a version or something else or however they do versioning.
I think this would provide more freedom and flexibility and would also be shorter to type.
We could still state that std.Version would be inspired by Semantic Versioning.
While we're at it, we could probably change the patch version components major, minor, and patch to be u16 because usize is just kind of weird IMO (why the size of a pointer; these components aren't used to index data) and if you have a component bigger than 65535 then you're doing something wrong anyway.
CC @andrewrk you probably have some opinions on this with your recent package-related changes.
If you want you can just merge this and I can open an issue for that to discuss that later.

@kcbanner
Copy link
Contributor

kcbanner commented Mar 4, 2023

Another PR of mine #14782 is hit with the exact same CI failure. Can anyone tell me the correct way to rebuild stage1/? I don't seem to be doing it correctly. Am I supposed to run zig build update-zig1 with or without --zig-lib-dir lib and am I supposed to use a Zig compiler built by following the steps https://github.com/ziglang/zig/wiki/Building-Zig-From-Source#option-a-use-your-system-installed-build-tools in this branch or can I also use a Zig compiler downloaded from https://ziglang.org/download? Anyway, should work just fine when I simply don't update stage1/zig1.wasm...

You only have to update zig1.wasm if the changes you made are required to build the compiler itself. I usually build zig stage4 (with a prebuilt or bootstrapped zig), then run zig build update-zig1 using that stage4 zig.

@perillo
Copy link
Contributor

perillo commented Apr 4, 2023

I think this PR can be closed.

@ghost
Copy link
Author

ghost commented Apr 8, 2023

Why?

@perillo
Copy link
Contributor

perillo commented Apr 9, 2023

Why?

I started searching the oldest PR to find if there where some abandoned one. From the title of the PR I thought the builtin was referring to the old compiler builtin module. My bad for not checking the code (and the date, since the move of builtin types to std was done in 2019).

Sorry for the noise.

@andrewrk andrewrk merged commit 6e84f46 into ziglang:master Jun 17, 2023
andrewrk added a commit to ziglang/libc-abi-tools that referenced this pull request Oct 12, 2023
This broke with ziglang/zig#13998 so I copied
the old Version struct into the file. `std.SemanticVersion` does not
support parsing e.g. "2.38" because it does not allow the implicit ".0"
to be dropped.
andrewrk pushed a commit that referenced this pull request Oct 23, 2023
In most cases "GLIBC_2.X" strings and `/lib/libc-2.x.so` files do not contain third (`patch`) field,
which causes std.SemanticVersion.parse function to return error. To fix this, we
reuse [now-public] std.zig.CrossTarget.parseVersion function,
which accounts for this third field and makes it 0 in case it was not found.
This new behaviour is similar to std.builtin.Version.parse, which was removed in
6e84f46

Fixes regression from 6e84f46
and #13998 .

Related: #17626 . Results with `zig end`:

Before: `"target": "x86_64-linux.6.5.7...6.5.7-gnu.2.19",`
After: `"target": "x86_64-linux.6.5.7...6.5.7-gnu.2.36",`

Also, while we are here, write explicit error sets and remove duplicate
logic from std.zig.system.darwin.macos.parseSystemVersion .

Signed-off-by: Eric Joldasov <bratishkaerik@getgoogleoff.me>
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

Successfully merging this pull request may close these issues.

remove either std.builtin.Version or std.SemanticVersion

5 participants