changeset 49656:010a1e73f69e stable

setup: further improve the error path for version retrieval This is a new take at the problem that 8d390a13474d tried to tackle. There was two issues after that previous improvement: - the 0.0+ version could survive a bit too long and reaching the installer version and staying there. - multiple use case where still failing. So the new code is better at: - always succeeding when running `make local` so that we can bootstrap a local version - no using that fallback outside of `make local` to avoid distribution of version with the buggy version number. The setup.py is a gigantic pile of spaghetti code, to the point where pastafarian pilgrim started knocking at its core. However I refrained from cleaning that up since the more to a `setup.cfg` means this code should be deleted soon™.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Fri, 17 Feb 2023 16:45:36 +0100
parents 5fb2546d0df1
children ca9d65d69c27
files Makefile setup.py
diffstat 2 files changed, 63 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Fri Feb 17 14:00:39 2023 +0100
+++ b/Makefile	Fri Feb 17 16:45:36 2023 +0100
@@ -58,7 +58,7 @@
 all: build doc
 
 local:
-	$(PYTHON) setup.py $(PURE) \
+	MERCURIAL_SETUP_MAKE_LOCAL=1 $(PYTHON) setup.py $(PURE) \
 	  build_py -c -d . \
 	  build_ext $(COMPILERFLAG) -i \
 	  build_hgexe $(COMPILERFLAG) -i \
--- a/setup.py	Fri Feb 17 14:00:39 2023 +0100
+++ b/setup.py	Fri Feb 17 16:45:36 2023 +0100
@@ -21,6 +21,11 @@
     return s.decode('latin-1')
 
 
+def eprint(*args, **kwargs):
+    kwargs['file'] = sys.stderr
+    print(*args, **kwargs)
+
+
 import ssl
 
 # ssl.HAS_TLSv1* are preferred to check support but they were added in Python
@@ -292,10 +297,11 @@
     if retcode == 0 and not filterhgerr(err):
         return hgcommand(hgcmd, hgenv)
 
-    raise SystemExit(
-        'Unable to find a working hg binary to extract the '
-        'version from the repository tags'
-    )
+    eprint("/!\\")
+    eprint(r"/!\ Unable to find a working hg binary")
+    eprint(r"/!\ Version cannot be extract from the repository")
+    eprint(r"/!\ Re-run the setup once a first version is built")
+    return None
 
 
 def localhgenv():
@@ -320,33 +326,46 @@
 
 version = ''
 
-if os.path.isdir('.hg'):
+
+def _try_get_version():
     hg = findhg()
+    if hg is None:
+        return ''
+    hgid = None
+    numerictags = []
     cmd = ['log', '-r', '.', '--template', '{tags}\n']
-    numerictags = [t for t in sysstr(hg.run(cmd)).split() if t[0:1].isdigit()]
+    pieces = sysstr(hg.run(cmd)).split()
+    numerictags = [t for t in pieces if t[0:1].isdigit()]
     hgid = sysstr(hg.run(['id', '-i'])).strip()
     if not hgid:
-        # Bail out if hg is having problems interacting with this repository,
-        # rather than falling through and producing a bogus version number.
-        # Continuing with an invalid version number will break extensions
-        # that define minimumhgversion.
-        raise SystemExit('Unable to determine hg version from local repository')
+        eprint("/!\\")
+        eprint(r"/!\ Unable to determine hg version from local repository")
+        eprint(r"/!\ Failed to retrieve current revision tags")
+        return ''
     if numerictags:  # tag(s) found
         version = numerictags[-1]
         if hgid.endswith('+'):  # propagate the dirty status to the tag
             version += '+'
-    else:  # no tag found
+    else:  # no tag found on the checked out revision
         ltagcmd = ['parents', '--template', '{latesttag}']
         ltag = sysstr(hg.run(ltagcmd))
         if not ltag:
-            ltag = 'null'
+            eprint("/!\\")
+            eprint(r"/!\ Unable to determine hg version from local repository")
+            eprint(
+                r"/!\ Failed to retrieve current revision distance to lated tag"
+            )
+            return ''
         changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
         changessince = len(hg.run(changessincecmd).splitlines())
-        if ltag == 'null':
-            ltag = '0.0'
         version = '%s+hg%s.%s' % (ltag, changessince, hgid)
     if version.endswith('+'):
         version = version[:-1] + 'local' + time.strftime('%Y%m%d')
+    return version
+
+
+if os.path.isdir('.hg'):
+    version = _try_get_version()
 elif os.path.exists('.hg_archival.txt'):
     kw = dict(
         [[t.strip() for t in l.split(':', 1)] for l in open('.hg_archival.txt')]
@@ -366,21 +385,35 @@
     with open('mercurial/__version__.py') as f:
         data = f.read()
     version = re.search('version = b"(.*)"', data).group(1)
-
-if version:
-    versionb = version
-    if not isinstance(versionb, bytes):
-        versionb = versionb.encode('ascii')
+if not version:
+    if os.environ.get("MERCURIAL_SETUP_MAKE_LOCAL") == "1":
+        version = "0.0+0"
+        eprint("/!\\")
+        eprint(r"/!\ Using '0.0+0' as the default version")
+        eprint(r"/!\ Re-run make local once that first version is built")
+        eprint("/!\\")
+    else:
+        eprint("/!\\")
+        eprint(r"/!\ Could not determine the Mercurial version")
+        eprint(r"/!\ You need to build a local version first")
+        eprint(r"/!\ Run `make local` and try again")
+        eprint("/!\\")
+        msg = "Run `make local` first to get a working local version"
+        raise SystemExit(msg)
 
-    write_if_changed(
-        'mercurial/__version__.py',
-        b''.join(
-            [
-                b'# this file is autogenerated by setup.py\n'
-                b'version = b"%s"\n' % versionb,
-            ]
-        ),
-    )
+versionb = version
+if not isinstance(versionb, bytes):
+    versionb = versionb.encode('ascii')
+
+write_if_changed(
+    'mercurial/__version__.py',
+    b''.join(
+        [
+            b'# this file is autogenerated by setup.py\n'
+            b'version = b"%s"\n' % versionb,
+        ]
+    ),
+)
 
 
 class hgbuild(build):