瀏覽代碼

Scons - add compiler requirements

lawnjelly 2 月之前
父節點
當前提交
4aa8ecd6be
共有 2 個文件被更改,包括 207 次插入0 次删除
  1. 68 0
      SConstruct
  2. 139 0
      methods.py

+ 68 - 0
SConstruct

@@ -469,6 +469,74 @@ if selected_platform in platform_list:
     if env["lto"] != "none":
         print("Using LTO: " + env["lto"])
 
+    # Enforce our minimal compiler version requirements
+    cc_version = methods.get_compiler_version_ex(env)
+    cc_version_major = cc_version["major"]
+    cc_version_minor = cc_version["minor"]
+    cc_version_metadata1 = cc_version["metadata1"]
+
+    if cc_version_major == -1:
+        print_warning(
+            "Couldn't detect compiler version, skipping version checks. "
+            "Build may fail if the compiler doesn't support C++17 fully."
+        )
+    elif methods.using_gcc(env):
+        if cc_version_major < 9:
+            print_error(
+                "Detected GCC version older than 9, which does not fully support "
+                "C++17, or has bugs when compiling Godot. Supported versions are 9 "
+                "and later. Use a newer GCC version, or Clang 6 or later by passing "
+                '"use_llvm=yes" to the SCons command line.'
+            )
+            Exit(255)
+        elif cc_version_metadata1 == "win32":
+            print_error(
+                "Detected mingw version is not using posix threads. Only posix "
+                "version of mingw is supported. "
+                'Use "update-alternatives --config x86_64-w64-mingw32-g++" '
+                "to switch to posix threads."
+            )
+            Exit(255)
+    elif methods.using_clang(env):
+        # Apple LLVM versions differ from upstream LLVM version \o/, compare
+        # in https://en.wikipedia.org/wiki/Xcode#Toolchain_versions
+        if methods.is_apple_clang(env):
+            if cc_version_major < 10:
+                print_error(
+                    "Detected Apple Clang version older than 10, which does not fully "
+                    "support C++17. Supported versions are Apple Clang 10 and later."
+                )
+                Exit(255)
+        else:
+            if cc_version_major < 6:
+                print_error(
+                    "Detected Clang version older than 6, which does not fully support "
+                    "C++17. Supported versions are Clang 6 and later."
+                )
+                Exit(255)
+
+    elif env.msvc:
+        # Ensure latest minor builds of Visual Studio 2017/2019.
+        # https://github.com/godotengine/godot/pull/94995#issuecomment-2336464574
+        if cc_version_major == 16 and cc_version_minor < 11:
+            print_error(
+                "Detected Visual Studio 2019 version older than 16.11, which has bugs "
+                "when compiling Godot. Use a newer VS2019 version, or VS2022."
+            )
+            Exit(255)
+        if cc_version_major == 15 and cc_version_minor < 9:
+            print_error(
+                "Detected Visual Studio 2017 version older than 15.9, which has bugs "
+                "when compiling Godot. Use a newer VS2017 version, or VS2019/VS2022."
+            )
+            Exit(255)
+        if cc_version_major < 15:
+            print_error(
+                "Detected Visual Studio 2015 or earlier, which is unsupported in Godot. "
+                "Supported versions are Visual Studio 2017 and later."
+            )
+            Exit(255)
+
     # Set our C and C++ standard requirements.
     # Prepending to make it possible to override
     # This needs to come after `configure`, otherwise we don't have env.msvc.

+ 139 - 0
methods.py

@@ -19,6 +19,9 @@ from os.path import normpath, basename
 # Get the "Godot" folder name ahead of time
 base_folder_path = str(os.path.abspath(Path(__file__).parent)) + "/"
 base_folder_only = os.path.basename(os.path.normpath(base_folder_path))
+
+compiler_version_cache = None
+
 # Listing all the folders we have converted
 # for SCU in scu_builders.py
 _scu_folders = set()
@@ -1068,6 +1071,21 @@ def detect_darwin_sdk_path(platform, env):
             raise
 
 
+def is_apple_clang(env):
+    import shlex
+
+    if env["platform"] not in ["macos", "ios"]:
+        return False
+    if not using_clang(env):
+        return False
+    try:
+        version = subprocess.check_output(shlex.split(env.subst(env["CXX"])) + ["--version"]).strip().decode("utf-8")
+    except (subprocess.CalledProcessError, OSError):
+        print_warning("Couldn't parse CXX environment variable to infer compiler version.")
+        return False
+    return version.startswith("Apple")
+
+
 def get_compiler_version(env):
     """
     Returns an array of version numbers as ints: [major, minor, patch].
@@ -1090,6 +1108,127 @@ def get_compiler_version(env):
         return None
 
 
+def get_compiler_version_ex(env):
+    """
+    Returns a dictionary with various version information:
+
+    - major, minor, patch: Version following semantic versioning system
+    - metadata1, metadata2: Extra information
+    - date: Date of the build
+    """
+
+    global compiler_version_cache
+    if compiler_version_cache is not None:
+        return compiler_version_cache
+
+    import shlex
+
+    ret = {
+        "major": -1,
+        "minor": -1,
+        "patch": -1,
+        "metadata1": "",
+        "metadata2": "",
+        "date": "",
+        "apple_major": -1,
+        "apple_minor": -1,
+        "apple_patch1": -1,
+        "apple_patch2": -1,
+        "apple_patch3": -1,
+    }
+
+    if env.msvc and not using_clang(env):
+        try:
+            # FIXME: `-latest` works for most cases, but there are edge-cases where this would
+            # benefit from a more nuanced search.
+            # https://github.com/godotengine/godot/pull/91069#issuecomment-2358956731
+            # https://github.com/godotengine/godot/pull/91069#issuecomment-2380836341
+            args = [
+                env["VSWHERE"],
+                "-latest",
+                "-prerelease",
+                "-products",
+                "*",
+                "-requires",
+                "Microsoft.Component.MSBuild",
+                "-utf8",
+            ]
+            version = subprocess.check_output(args, encoding="utf-8").strip()
+            for line in version.splitlines():
+                split = line.split(":", 1)
+                if split[0] == "catalog_productDisplayVersion":
+                    sem_ver = split[1].split(".")
+                    ret["major"] = int(sem_ver[0])
+                    ret["minor"] = int(sem_ver[1])
+                    ret["patch"] = int(sem_ver[2].split()[0])
+                # Could potentially add section for determining preview version, but
+                # that can wait until metadata is actually used for something.
+                if split[0] == "catalog_buildVersion":
+                    ret["metadata1"] = split[1]
+        except (subprocess.CalledProcessError, OSError):
+            print_warning("Couldn't find vswhere to determine compiler version.")
+        return update_compiler_version_cache(ret)
+
+    # Not using -dumpversion as some GCC distros only return major, and
+    # Clang used to return hardcoded 4.2.1: # https://reviews.llvm.org/D56803
+    try:
+        version = subprocess.check_output(
+            shlex.split(env.subst(env["CXX"]), posix=False) + ["--version"], shell=(os.name == "nt"), encoding="utf-8"
+        ).strip()
+    except (subprocess.CalledProcessError, OSError):
+        print_warning("Couldn't parse CXX environment variable to infer compiler version.")
+        return update_compiler_version_cache(ret)
+
+    match = re.search(
+        r"(?:(?<=version )|(?<=\) )|(?<=^))"
+        r"(?P<major>\d+)"
+        r"(?:\.(?P<minor>\d*))?"
+        r"(?:\.(?P<patch>\d*))?"
+        r"(?:-(?P<metadata1>[0-9a-zA-Z-]*))?"
+        r"(?:\+(?P<metadata2>[0-9a-zA-Z-]*))?"
+        r"(?: (?P<date>[0-9]{8}|[0-9]{6})(?![0-9a-zA-Z]))?",
+        version,
+    )
+    if match is not None:
+        for key, value in match.groupdict().items():
+            if value is not None:
+                ret[key] = value
+
+    match_apple = re.search(
+        r"(?:(?<=clang-)|(?<=\) )|(?<=^))"
+        r"(?P<apple_major>\d+)"
+        r"(?:\.(?P<apple_minor>\d*))?"
+        r"(?:\.(?P<apple_patch1>\d*))?"
+        r"(?:\.(?P<apple_patch2>\d*))?"
+        r"(?:\.(?P<apple_patch3>\d*))?",
+        version,
+    )
+    if match_apple is not None:
+        for key, value in match_apple.groupdict().items():
+            if value is not None:
+                ret[key] = value
+
+    # Transform semantic versioning to integers
+    for key in [
+        "major",
+        "minor",
+        "patch",
+        "apple_major",
+        "apple_minor",
+        "apple_patch1",
+        "apple_patch2",
+        "apple_patch3",
+    ]:
+        ret[key] = int(ret[key] or -1)
+    return update_compiler_version_cache(ret)
+
+
+def update_compiler_version_cache(value):
+    global compiler_version_cache
+    compiler_version_cache = value
+    return value
+
+
 def is_vanilla_clang(env):
     if not using_clang(env):
         return False