diff --git a/methods.py b/methods.py index 428405fb04..77a1085baf 100644 --- a/methods.py +++ b/methods.py @@ -1,8 +1,10 @@ +import collections import os import re import glob import subprocess from collections import OrderedDict +from typing import Iterator # We need to define our own `Action` method to control the verbosity of output # and whenever we need to run those commands in a subprocess on some platforms. @@ -604,7 +606,11 @@ def detect_visual_c_compiler_version(tools_env): def find_visual_c_batch_file(env): - from SCons.Tool.MSCommon.vc import get_default_version, get_host_target, find_batch_file + from SCons.Tool.MSCommon.vc import ( + get_default_version, + get_host_target, + find_batch_file, + ) version = get_default_version(env) (host_platform, target_platform, _) = get_host_target(env) @@ -656,36 +662,107 @@ def generate_vs_project(env, num_jobs): batch_file = find_visual_c_batch_file(env) if batch_file: - def build_commandline(commands): - common_build_prefix = [ - 'cmd /V /C set "plat=$(PlatformTarget)"', - '(if "$(PlatformTarget)"=="x64" (set "plat=x86_amd64"))', - 'set "tools=%s"' % env["tools"], - '(if "$(Configuration)"=="release" (set "tools=no"))', - 'call "' + batch_file + '" !plat!', - ] + class ModuleConfigs(collections.Mapping): + # This version information (Win32, x64, Debug, Release, Release_Debug seems to be + # required for Visual Studio to understand that it needs to generate an NMAKE + # project. Do not modify without knowing what you are doing. + PLATFORMS = ["Win32", "x64"] + PLATFORM_IDS = ["32", "64"] + CONFIGURATIONS = ["debug", "release", "release_debug"] + CONFIGURATION_IDS = ["tools", "opt", "opt.tools"] - # Windows allows us to have spaces in paths, so we need - # to double quote off the directory. However, the path ends - # in a backslash, so we need to remove this, lest it escape the - # last double quote off, confusing MSBuild - common_build_postfix = [ - "--directory=\"$(ProjectDir.TrimEnd('\\'))\"", - "platform=windows", - "target=$(Configuration)", - "progress=no", - "tools=!tools!", - "-j%s" % num_jobs, - ] + @staticmethod + def for_every_variant(value): + return [value for _ in range(len(ModuleConfigs.CONFIGURATIONS) * len(ModuleConfigs.PLATFORMS))] - if env["tests"]: - common_build_postfix.append("tests=yes") + def __init__(self): - if env["custom_modules"]: - common_build_postfix.append("custom_modules=%s" % env["custom_modules"]) + shared_targets_array = [] + self.names = [] + self.arg_dict = { + "variant": [], + "runfile": shared_targets_array, + "buildtarget": shared_targets_array, + "cpppaths": [], + "cppdefines": [], + "cmdargs": [], + } + self.add_mode() # default - result = " ^& ".join(common_build_prefix + [" ".join([commands] + common_build_postfix)]) - return result + def add_mode( + self, + name: str = "", + includes: str = "", + cli_args: str = "", + defines=None, + ): + if defines is None: + defines = [] + self.names.append(name) + self.arg_dict["variant"] += [ + f'{config}{f"_[{name}]" if name else ""}|{platform}' + for config in ModuleConfigs.CONFIGURATIONS + for platform in ModuleConfigs.PLATFORMS + ] + self.arg_dict["runfile"] += [ + f'bin\\godot.windows.{config_id}.{plat_id}{f".{name}" if name else ""}.exe' + for config_id in ModuleConfigs.CONFIGURATION_IDS + for plat_id in ModuleConfigs.PLATFORM_IDS + ] + self.arg_dict["cpppaths"] += ModuleConfigs.for_every_variant(env["CPPPATH"] + [includes]) + self.arg_dict["cppdefines"] += ModuleConfigs.for_every_variant(env["CPPDEFINES"] + defines) + self.arg_dict["cmdargs"] += ModuleConfigs.for_every_variant(cli_args) + + def build_commandline(self, commands): + + configuration_getter = ( + "$(Configuration" + + "".join([f'.Replace("{name}", "")' for name in self.names[1:]]) + + '.Replace("_[]", "")' + + ")" + ) + + common_build_prefix = [ + 'cmd /V /C set "plat=$(PlatformTarget)"', + '(if "$(PlatformTarget)"=="x64" (set "plat=x86_amd64"))', + 'set "tools=%s"' % env["tools"], + f'(if "{configuration_getter}"=="release" (set "tools=no"))', + 'call "' + batch_file + '" !plat!', + ] + + # Windows allows us to have spaces in paths, so we need + # to double quote off the directory. However, the path ends + # in a backslash, so we need to remove this, lest it escape the + # last double quote off, confusing MSBuild + common_build_postfix = [ + "--directory=\"$(ProjectDir.TrimEnd('\\'))\"", + "platform=windows", + f"target={configuration_getter}", + "progress=no", + "tools=!tools!", + "-j%s" % num_jobs, + ] + + if env["tests"]: + common_build_postfix.append("tests=yes") + + if env["custom_modules"]: + common_build_postfix.append("custom_modules=%s" % env["custom_modules"]) + + result = " ^& ".join(common_build_prefix + [" ".join([commands] + common_build_postfix)]) + return result + + # Mappings interface definitions + + def __iter__(self) -> Iterator[str]: + for x in self.arg_dict: + yield x + + def __len__(self) -> int: + return len(self.names) + + def __getitem__(self, k: str): + return self.arg_dict[k] add_to_vs_project(env, env.core_sources) add_to_vs_project(env, env.drivers_sources) @@ -700,21 +777,24 @@ def generate_vs_project(env, num_jobs): for header in glob_recursive("**/*.h"): env.vs_incs.append(str(header)) - env["MSVSBUILDCOM"] = build_commandline("scons") - env["MSVSREBUILDCOM"] = build_commandline("scons vsproj=yes") - env["MSVSCLEANCOM"] = build_commandline("scons --clean") + module_configs = ModuleConfigs() + import modules.mono.build_scripts.mono_reg_utils as mono_reg - # This version information (Win32, x64, Debug, Release, Release_Debug seems to be - # required for Visual Studio to understand that it needs to generate an NMAKE - # project. Do not modify without knowing what you are doing. - debug_variants = ["debug|Win32"] + ["debug|x64"] - release_variants = ["release|Win32"] + ["release|x64"] - release_debug_variants = ["release_debug|Win32"] + ["release_debug|x64"] - variants = debug_variants + release_variants + release_debug_variants - debug_targets = ["bin\\godot.windows.tools.32.exe"] + ["bin\\godot.windows.tools.64.exe"] - release_targets = ["bin\\godot.windows.opt.32.exe"] + ["bin\\godot.windows.opt.64.exe"] - release_debug_targets = ["bin\\godot.windows.opt.tools.32.exe"] + ["bin\\godot.windows.opt.tools.64.exe"] - targets = debug_targets + release_targets + release_debug_targets + if env.get("module_mono_enabled"): + mono_root = env.get("mono_prefix") or mono_reg.find_mono_root_dir(env["bits"]) + if mono_root: + module_configs.add_mode( + "mono", + includes=os.path.join(mono_root, "include", "mono-2.0"), + cli_args="module_mono_enabled=yes mono_glue=yes", + defines=[("MONO_GLUE_ENABLED",)], + ) + else: + print("Mono installation directory not found. Generated project will not have build variants for Mono.") + + env["MSVSBUILDCOM"] = module_configs.build_commandline("scons") + env["MSVSREBUILDCOM"] = module_configs.build_commandline("scons vsproj=yes") + env["MSVSCLEANCOM"] = module_configs.build_commandline("scons --clean") if not env.get("MSVS"): env["MSVS"]["PROJECTSUFFIX"] = ".vcxproj" env["MSVS"]["SOLUTIONSUFFIX"] = ".sln" @@ -722,10 +802,8 @@ def generate_vs_project(env, num_jobs): target=["#godot" + env["MSVSPROJECTSUFFIX"]], incs=env.vs_incs, srcs=env.vs_srcs, - runfile=targets, - buildtarget=targets, auto_build_solution=1, - variant=variants, + **module_configs, ) else: print("Could not locate Visual Studio batch file to set up the build environment. Not generating VS project.")