unit-test: meson: check meson version for C++20

If a package requests C++20 support, only Meson >=0.57 recognizes
the 'cpp_std=c++20' option but doesn't warn about this.  Add code
in our test path to ensure that if a package has requested C++20
it also requests Meson >=0.57.  This helps prevent a cryptic
message if someone compiles one of our packages on a distro with
an older version of Meson.

Tested: Ran script against sdbusplus with various settings of
meson_version and cpp_std.

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I130766a822710bf634eca3677e22c9d484defa00
diff --git a/scripts/unit-test.py b/scripts/unit-test.py
index 18e4730..44e3a37 100755
--- a/scripts/unit-test.py
+++ b/scripts/unit-test.py
@@ -10,6 +10,7 @@
 from git import Repo
 from mesonbuild import coredata, optinterpreter
 from mesonbuild.mesonlib import OptionKey
+from mesonbuild.mesonlib import version_compare as meson_version_compare
 from urllib.parse import urljoin
 from subprocess import check_call, call, CalledProcessError
 import os
@@ -896,6 +897,14 @@
         check_call_cmd('sudo', '-n', '--', 'ninja', '-C', 'build', 'install')
 
     def test(self):
+        # It is useful to check various settings of the meson.build file
+        # for compatibility, such as meson_version checks.  We shouldn't
+        # do this in the configure path though because it affects subprojects
+        # and dependencies as well, but we only want this applied to the
+        # project-under-test (otherwise an upstream dependency could fail
+        # this check without our control).
+        self._extra_meson_checks()
+
         try:
             test_args = ('--repeat', str(args.repeat), '-C', 'build')
             check_call_cmd('meson', 'test', *test_args)
@@ -1001,6 +1010,29 @@
                        '-Db_coverage=false')
         run_cppcheck()
 
+    def _extra_meson_checks(self):
+        with open(os.path.join(self.path, 'meson.build'), 'rt') as f:
+            build_contents = f.read()
+
+        # Find project's specified meson_version.
+        meson_version = None
+        pattern = r"meson_version:[^']*'([^']*)'"
+        for match in re.finditer(pattern, build_contents):
+            group = match.group(1)
+            meson_version = group
+
+        # C++20 requires at least Meson 0.57 but Meson itself doesn't
+        # identify this.  Add to our unit-test checks so that we don't
+        # get a meson.build missing this.
+        pattern = r"'cpp_std=c\+\+20'"
+        for match in re.finditer(pattern, build_contents):
+            if not meson_version or \
+                    not meson_version_compare(meson_version, ">=0.57"):
+                raise Exception(
+                    "C++20 support requires specifying in meson.build: "
+                    + "meson_version: '>=0.57'"
+                )
+
 
 class Package(object):
     def __init__(self, name=None, path=None):