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

build: use a more user-friendly version string for shallow clones #15792

Merged
merged 2 commits into from
Feb 5, 2025

Conversation

Dudemanguy
Copy link
Member

Also for release tarball builds, put a 'v' in front of the number so it matches the formatting of the git tags.

Copy link

github-actions bot commented Feb 2, 2025

Download the artifacts for this pull request:

Windows
macOS

if is_git.returncode() == 0 and has_tag.returncode() != 0
# put the mpv version and hash together
mpv_version = 'v' + meson.project_version().replace('-UNKNOWN', '')
hash = run_command([git_args, 'rev-parse', '--short', '@'],
Copy link
Contributor

Choose a reason for hiding this comment

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

this will only work if meson configure is run.

Copy link
Member Author

Choose a reason for hiding this comment

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

Well yeah the hash doesn't update with new commits if you're doing development on a shallow clone but nobody does that right? Could write a python script I guess.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's less than 10 lines as an inline script so not too bad.

Copy link
Contributor

Choose a reason for hiding this comment

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

It might be all in python to avoid mixing meson/python logic.

@Dudemanguy Dudemanguy force-pushed the version branch 3 times, most recently from 9b1c83e to e5697f1 Compare February 2, 2025 22:15
@kasper93
Copy link
Contributor

kasper93 commented Feb 2, 2025

I'm not a fan of v0.00.0-hash format, it implies that this is this exact version and hash is additional information, but without checking the code it is ambiguous what it means. Is this release version or not?

it's not exactly helpful when wanting to know the general version at a glance

imho it's still not helpful. The information that it's is v0.39.0 is not valuable, when we are half a year in the future, with over 700 commits on top. mpv-version is documented correctly.

@eli-schwartz
Copy link

Well, the stated rationale of the change is to fix software that is doing a version comparison to see if your mpv version is "new enough to support XXX features", no?

Given that constraint, it doesn't matter how many commits you have on top. The stated objective can still be achieved.

@kasper93
Copy link
Contributor

kasper93 commented Feb 3, 2025

Well, the stated rationale of the change is to fix software that is doing a version comparison to see if your mpv version is "new enough to support XXX features", no?

We never agreed to this contract, and the documentation clearly states that mpv-version may or may not include a number. Any software that explicitly checks for this and expects otherwise is at fault.

I have no objection to improving the displayed version, but it should remain purely informational and not be used for feature testing. The mpv API has a defined version in client.h

mpv/libmpv/client.h

Lines 250 to 251 in 38ad1ed

#define MPV_MAKE_VERSION(major, minor) (((major) << 16) | (minor) | 0UL)
#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(2, 5)
which should be used instead.

As for the rest of client interface, each property or option operation will return an appropriate error code if it does not exist. We do not increment a "number" when interface changes are made, such as adding or removing properties or options. Instead, you can query whether your current mpv supports a given property.

Given that constraint, it doesn't matter how many commits you have on top. The stated objective can still be achieved.

It solves one specific scenario only. And ignores the fact that in those "many commits" properties may have been removed and you are blindsided. You are already using git version of the software, don't expect that v0.39.0 is the same as v0.39.0 + 723 commits... We have mechanism to check for properties availability.

(or if you want to use new feature in your dev build, do you wait for v0.40.0 to be released?, so that your version check "works")

Back to the PR changes itself, I find describe output way more clear v0.39.0-723-g38ad1ed03b and I understand with shallow clones we don't have all information to produce that, but maybe v0.39.0-dev-g38ad1ed03b would work? Also it is quite common to bump version number after release, so that it would be v0.40.0-dev ... to indicated that it is 0.40 release cycle.

@Dudemanguy
Copy link
Member Author

I don't really think the commit number is meaningful personally. In fact, I had to look up what that part of the string meant from git describe since I didn't even know off the top of my head. But sure, I can just add 'dev' to the string to clear things up.

@eli-schwartz
Copy link

eli-schwartz commented Feb 3, 2025

I have no objection to improving the displayed version, but it should remain purely informational and not be used for feature testing. The mpv API has a defined version in client.h

... and clearly, anyone who wishes to retrieve information about what functionality is available in /usr/bin/mpv shall be required to do so by compiling a program against client.h?

The application from the issue report is a javascript application btw, which tells users to download mpv.app from https://mpv.io/installation/ -- so it's not clear to me how you expect them to do feature testing with client.h even if they wanted to.

It's not like the mpv.app downloadable bundle actually includes client.h or anything...

@kasper93
Copy link
Contributor

kasper93 commented Feb 3, 2025

I have no objection to improving the displayed version, but it should remain purely informational and not be used for feature testing. The mpv API has a defined version in client.h

... and clearly, anyone who wishes to retrieve information about what functionality is available in /usr/bin/mpv shall be required to do so by compiling a program against client.h?

The application from the issue report is a javascript application btw, which tells users to download mpv.app from https://mpv.io/installation/ -- so it's not clear to me how you expect them to do feature testing with client.h even if they wanted to.

It's not like the mpv.app downloadable bundle actually includes client.h or anything...

javascript and lua interface don't need any version checking, you can test for existence of functions / properties / options. client.h exposes version, because it is important for building/linking C interface.

common/meson.build Outdated Show resolved Hide resolved
'--git-dir=' + join_paths(source_root, '.git'),
'--work-tree=' + source_root,
'describe', '--always', '--tags', '--dirty'],
command: version_command,
Copy link
Contributor

@kasper93 kasper93 Feb 5, 2025

Choose a reason for hiding this comment

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

I did some bikeshedding, feel free to use any if you like.

git = find_program('git', required: false)

generate_ver = '''
#!/usr/bin/env python3
import os
import sys
from subprocess import DEVNULL, check_output
git_cmd = [sys.argv[1], "--git-dir=" + os.path.join(sys.argv[2], ".git"),
           "--work-tree=" + sys.argv[2]]
describe_cmd = git_cmd + ["describe", "--abbrev=9", "--tags", "--dirty"]
try:
    ver = check_output(describe_cmd, stderr=DEVNULL, encoding="UTF-8")
except Exception:
    describe_cmd += ["--always"]
    ver = check_output(describe_cmd, stderr=DEVNULL, encoding="UTF-8")
    ver = f"v{sys.argv[3]}-dev-g{ver}"
sys.stdout.write(ver)
'''

if not git.found()
    git = ''
endif

version_h = vcs_tag(
    command: [python, '-c', generate_ver, git, source_root, meson.project_version().replace('-UNKNOWN', '')],
    fallback: 'v' + meson.project_version(),
    input: 'version.h.in',
    output: 'version.h',
    replace_string: '@VERSION@',
)

sources += version_h

Copy link
Contributor

Choose a reason for hiding this comment

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

btw. meson uses --dirty=+, not sure I like this very much, but it's more concise than default -dirty appended.

Copy link
Member Author

Choose a reason for hiding this comment

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

If you insist on making it completely in python, that is fine but we should just reintroduce version.py at that point.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't insist in making it in python. I would like to avoid it completely. Though even in your solution it is unavoidable.

I don't like the fragmentation of current solution. Particularly, we have describe command in 3 places. And rev-parse on top of that. Also checks run in meson may become stale, if say someone unhallow the repo.

I also want to make sure we have consistent naming in each case.
v0.39.0
v0.39.0-UNKNOWN // we could even replace our UNKNOWN with dev to be concise. I think UNKNOWN is bit obnoxious :) (why all caps?)
v0.39.0-dev-ge7852e984
v0.39.0-801-ge7852e984
v0.39.0-801-ge7852e984-dirty

And this also require --abbrev=9, which would need to be repeated 2 times again.

we should just reintroduce version.py at that point.

I see where you coming from, but it's still a simple wrapper over git describe. Also by doing it inline we can skip some checks of input arguments, because we know how it is called. Standalone script would be more bloated in arg parsing.

Copy link
Member Author

@Dudemanguy Dudemanguy Feb 5, 2025

Choose a reason for hiding this comment

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

Evidently, printing a number is a hard problem. An inline is OK if it is short and sweet but since we have to use try/except I am going to bring back version.py again. It's just more readable that way and there's yet another condition to control for if we're doing this in all in python.

@odrling
Copy link

odrling commented Feb 5, 2025

javascript and lua interface don't need any version checking, you can test for existence of functions / properties / options.

That’s true but I’m fairly sure this check was added with 0.39 when the loadfile syntax changed. Now they could have tried both syntaxes at run time and found out which one works or used named arguments if support for versions before they were introduced doesn’t matter (which is hard to tell until you get a bug report from an Ubuntu 20.04 user).

So yes, it can be done another way, but having to keep compatibility with two different syntaxes of the command that plays a file is an awkward situation. And when projects get a report that mpv 0.39 breaks their app, it is fairly obvious for them to just check the version string... and run into this issue a few months later.

Also the C client API version can be queried from the JSON IPC (with get-version), but command changes are not tracked in the client API changelog, which is why it’s not really useful here.

TOOLS/version.py Outdated
"--work-tree=" + sys.argv[2]]

try:
check_output(git_cmd + ["rev-parse"], stderr=DEVNULL, encoding="UTF-8")
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is necessary, describe will fail accordingly if needed.

Copy link
Member Author

@Dudemanguy Dudemanguy Feb 5, 2025

Choose a reason for hiding this comment

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

The exception will have an exception if git is not present but we can just early exit instead.

Edit: Actually no we can't because git could be there but it will still fail if it is a source tarball.

Choose a reason for hiding this comment

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

That looks like a very complicated and exception-handler-laden way of writing this:

r = subprocess.run(git_cmd + ['rev-parse'], capture_output=True, text=True)

if r.returncode != 0:
    sys.exit(1)

r = subprocess.run(git_describe, capture_output=True, text=True)

if r.returncode == 0:
    ver = r.stdout
else:
    r = subprocess.run(git_describe + ['--always'], capture_output=True, text=True)
    if r.returncode != 0:
        sys.exit('inconceivable! rev-parse succeeded, but git describe --always failed??')
    ver = r.stdout
    ver = f'v{sys.argv[3]}-dev-g{ver}'

Copy link
Contributor

Choose a reason for hiding this comment

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

two empty lines after imports

Copy link
Contributor

Choose a reason for hiding this comment

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

That looks like a very complicated and exception-handler-laden way of writing this:

And what is better with dummy error code checks?

Choose a reason for hiding this comment

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

Exceptions should be, well, exceptional.

Copy link
Contributor

Choose a reason for hiding this comment

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

The exception will have an exception if git is not present but we can just early exit instead.

If there is exception (any) meson will fallback to fallback version.

Edit: Actually no we can't because git could be there but it will still fail if it is a source tarball.

What do you mean? If there is no git directory it will throw exception and meson will to fallback version.

See, you are optimizing for failure case, which is not what imho is the nominal case.

This dummy "early exit" rev-parse will force always at least 2 git commands to be executed. In case of shallow clone, it would be 3 commands. And in case when there is no git it would be 1.

What I propose is to skip rev-parse and let describe error out accordingly. This makes 1 single git command to be executed and at worse 2 if there are some errors, same in all other cases, shallow, non-git and so on.

Copy link
Member Author

@Dudemanguy Dudemanguy Feb 5, 2025

Choose a reason for hiding this comment

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

What I propose is to skip rev-parse and let describe error out accordingly

That doesn't work because a source tarball is not a git directory. The git describe will fail to the exception case which will fail again which floods the user terminal with traceback. I don't think you can disable the traceback in this case. Well maybe you can, but probably you shouldn't. At least two git calls are going to be needed.

Copy link
Member Author

@Dudemanguy Dudemanguy Feb 5, 2025

Choose a reason for hiding this comment

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

floods the user terminal with traceback

But that doesn't happen if you do it inline within meson so I guess you were right after all.

Copy link
Contributor

Choose a reason for hiding this comment

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

Meson wraps this command and catch any abnormal command exit and fallbacks to fallback: 'v' + meson.project_version(),

The git describe will fail to the exception case which will fail again

the fail again case, can be fixed by more strict except ...: which would catch only if the command returned error, and pass through if command doesn't exist.

I don't think git returns different return codes for "not repository" or "no tags found" and also checking return codes is not really portable between posix and windows as the values will differ on the same logical errors.

TOOLS/version.py Outdated
#!/usr/bin/env python3
import os
import sys
from subprocess import check_output, DEVNULL
Copy link
Contributor

Choose a reason for hiding this comment

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

two new lines after imports

@Dudemanguy Dudemanguy force-pushed the version branch 7 times, most recently from c5780a0 to 243a363 Compare February 5, 2025 21:40
This was an inconsistency with the git tagging since the meson project
version is just a number.
If the cloned repo is shallow (e.g. like the default github actions
settings), the git describe command won't actually be able to fetch any
tags and will instead just get a hash. The resulting binary version
string will end up being that git hash. While it does tell you the exact
commit, it's not exactly helpful when wanting to know the general
version at a glance. For the case where we do have a git directory but
no available tags, build a version string using the mpv version + the
commit hash.

Fixes mpv-player#15789.
@Dudemanguy
Copy link
Member Author

Oh well, at least I discovered that our linter sucked in all of this mess. I'll merge kasper's suggestion soon. More than enough time was spent on this.

@Dudemanguy Dudemanguy merged commit 05ff82e into mpv-player:master Feb 5, 2025
27 checks passed
@Dudemanguy Dudemanguy deleted the version branch February 5, 2025 23:17
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.

4 participants