diff --git a/.editorconfig b/.editorconfig index 63251a3b296..c79c816e35c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,3 +15,7 @@ indent_style = space [{*.{yml,yaml},.clang{-format,-tidy,d}}] indent_size = 2 indent_style = space + +[{*.props,*.vcxproj}] +indent_size = 2 +indent_style = space diff --git a/methods.py b/methods.py index dd7a46e05ed..cb3fb118587 100644 --- a/methods.py +++ b/methods.py @@ -14,6 +14,7 @@ from pathlib import Path from typing import Generator, List, Optional, Union, cast from misc.utility.color import print_error, print_info, print_warning +from platform_methods import detect_arch # Get the "Godot" folder name ahead of time base_folder = Path(__file__).resolve().parent @@ -1067,8 +1068,22 @@ def generate_vs_project(env, original_args, project_name="godot"): platform = env["platform"] target = env["target"] arch = env["arch"] + host_arch = detect_arch() + + host_platform = "windows" + if ( + sys.platform.startswith("linux") + or sys.platform.startswith("dragonfly") + or sys.platform.startswith("freebsd") + or sys.platform.startswith("netbsd") + or sys.platform.startswith("openbsd") + ): + host_platform = "linuxbsd" + elif sys.platform == "darwin": + host_platform = "macos" vs_configuration = {} + host_vs_configuration = {} common_build_prefix = [] confs = [] for x in sorted(glob.glob("platform/*")): @@ -1097,6 +1112,12 @@ def generate_vs_project(env, original_args, project_name="godot"): if platform == platform_name: common_build_prefix = msvs.get_build_prefix(env) vs_configuration = vsconf + if platform_name == host_platform: + host_vs_configuration = vsconf + for a in vsconf["arches"]: + if host_arch == a["architecture"]: + host_arch = a["platform"] + break except Exception: pass @@ -1254,12 +1275,11 @@ def generate_vs_project(env, original_args, project_name="godot"): properties.append( ";%s;" % (x, ";".join(itemlist[x]), x) ) - output = f"bin\\godot{env['PROGSUFFIX']}" + output = os.path.join("bin", f"godot{env['PROGSUFFIX']}") with open("misc/msvs/props.template", "r", encoding="utf-8") as file: props_template = file.read() - props_template = props_template.replace("%%VSCONF%%", vsconf) props_template = props_template.replace("%%CONDITION%%", condition) props_template = props_template.replace("%%PROPERTIES%%", "\n ".join(properties)) props_template = props_template.replace("%%EXTRA_ITEMS%%", "\n ".join(extraItems)) @@ -1272,6 +1292,7 @@ def generate_vs_project(env, original_args, project_name="godot"): proplist = [str(j) for j in env["CPPPATH"]] proplist += [str(j) for j in env.get("VSHINT_INCLUDES", [])] + proplist += [str(j) for j in get_default_include_paths(env)] props_template = props_template.replace("%%INCLUDES%%", ";".join(proplist)) proplist = env["CCFLAGS"] @@ -1307,17 +1328,17 @@ def generate_vs_project(env, original_args, project_name="godot"): commands = "scons" if len(common_build_prefix) == 0: - commands = "echo Starting SCons && cmd /V /C " + commands + commands = "echo Starting SCons & " + commands else: - common_build_prefix[0] = "echo Starting SCons && cmd /V /C " + common_build_prefix[0] + common_build_prefix[0] = "echo Starting SCons & " + common_build_prefix[0] - cmd = " ^& ".join(common_build_prefix + [" ".join([commands] + common_build_postfix)]) + cmd = " ".join(common_build_prefix + [" ".join([commands] + common_build_postfix)]) props_template = props_template.replace("%%BUILD%%", cmd) - cmd = " ^& ".join(common_build_prefix + [" ".join([commands] + cmd_rebuild)]) + cmd = " ".join(common_build_prefix + [" ".join([commands] + cmd_rebuild)]) props_template = props_template.replace("%%REBUILD%%", cmd) - cmd = " ^& ".join(common_build_prefix + [" ".join([commands] + cmd_clean)]) + cmd = " ".join(common_build_prefix + [" ".join([commands] + cmd_clean)]) props_template = props_template.replace("%%CLEAN%%", cmd) with open( @@ -1348,18 +1369,45 @@ def generate_vs_project(env, original_args, project_name="godot"): section2 = [] for conf in confs: godot_platform = conf["platform"] + has_editor = "editor" in conf["targets"] + + # Skip any platforms that can build the editor and don't match the host platform. + # + # When both Windows and Mac define an editor target, it's defined as platform+target+arch (windows+editor+x64 for example). + # VS only supports two attributes, a "Configuration" and a "Platform", and we currently map our target to the Configuration + # (i.e. editor/template_debug/template_release), and our architecture to the "Platform" (i.e. x64, arm64, etc). + # Those two are not enough to disambiguate multiple godot targets for different godot platforms with the same architecture, + # i.e. editor|x64 would currently match both windows editor intel 64 and linux editor intel 64. + # + # TODO: More work is needed in order to support generating VS projects that unambiguously support all platform+target+arch variations. + # The VS "Platform" has to be a known architecture that VS recognizes, so we can only play around with the "Configuration" part of the combo. + if has_editor and godot_platform != host_vs_configuration["platform"]: + continue + for p in conf["arches"]: sln_plat = p["platform"] proj_plat = sln_plat godot_arch = p["architecture"] - # Redirect editor configurations for non-Windows platforms to the Windows one, so the solution has all the permutations - # and VS doesn't complain about missing project configurations. + # Redirect editor configurations for platforms that don't support the editor target to the default editor target on the + # active host platform, so the solution has all the permutations and VS doesn't complain about missing project configurations. # These configurations are disabled, so they show up but won't build. - if godot_platform != "windows": + if not has_editor: section1 += [f"editor|{sln_plat} = editor|{proj_plat}"] - section2 += [ - f"{{{proj_uuid}}}.editor|{proj_plat}.ActiveCfg = editor|{proj_plat}", + section2 += [f"{{{proj_uuid}}}.editor|{proj_plat}.ActiveCfg = editor|{host_arch}"] + + configurations += [ + f'', + " editor", + f" {proj_plat}", + "", + ] + + properties += [ + f"", + " editor", + f" {proj_plat}", + "", ] for t in conf["targets"]: @@ -1383,21 +1431,6 @@ def generate_vs_project(env, original_args, project_name="godot"): "", ] - if godot_platform != "windows": - configurations += [ - f'', - " editor", - f" {proj_plat}", - "", - ] - - properties += [ - f"", - " editor", - f" {proj_plat}", - "", - ] - p = f"{project_name}.{godot_platform}.{godot_target}.{godot_arch}.generated.props" imports += [ f'' @@ -1606,3 +1639,18 @@ def to_raw_cstring(value: Union[str, List[str]]) -> str: else: # Wrap multiple segments in parenthesis to suppress `string-concatenation` warnings on clang. return "({})".format(" ".join(f'R"({segment.decode()})"' for segment in split)) + + +def get_default_include_paths(env): + if env.msvc: + return [] + compiler = env.subst("$CXX") + target = os.path.join(env.Dir("#main").abspath, "main.cpp") + args = [compiler, target, "-x", "c++", "-v"] + ret = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) + output = ret.stdout + match = re.search(r"#include <\.\.\.> search starts here:([\S\s]*)End of search list.", output) + if not match: + print_warning("Failed to find the include paths in the compiler output.") + return [] + return [x.strip() for x in match[1].strip().splitlines()] diff --git a/misc/msvs/nmake.substitution.props b/misc/msvs/nmake.substitution.props new file mode 100644 index 00000000000..05a155eb9eb --- /dev/null +++ b/misc/msvs/nmake.substitution.props @@ -0,0 +1,19 @@ + + + + + + Unknown + $(NMakeOutput) + + + + + + + + + + + + diff --git a/misc/msvs/props.template b/misc/msvs/props.template index 82e3e81f182..57483703056 100644 --- a/misc/msvs/props.template +++ b/misc/msvs/props.template @@ -1,6 +1,6 @@ - + %%BUILD%% %%REBUILD%% %%CLEAN%% @@ -18,6 +18,9 @@ %%EXTRA_ITEMS%% + + + + $(SolutionDir)\bin\$(GodotPlatform)\$(GodotConfiguration)\ obj\$(GodotPlatform)\$(GodotConfiguration)\ $(OutDir)\Layout - + - + + %%IMPORTS%% <_ProjectFileVersion>10.0.30319.1 - %%IMPORTS%% %%DEFAULT_ITEMS%% - + diff --git a/platform/linuxbsd/msvs.py b/platform/linuxbsd/msvs.py new file mode 100644 index 00000000000..5dae4e4781c --- /dev/null +++ b/platform/linuxbsd/msvs.py @@ -0,0 +1,11 @@ +# Tuples with the name of the arch +def get_platforms(): + return [("x64", "x86_64")] + + +def get_configurations(): + return ["editor", "template_debug", "template_release"] + + +def get_build_prefix(env): + return [] diff --git a/platform/macos/msvs.py b/platform/macos/msvs.py new file mode 100644 index 00000000000..39bc3c59748 --- /dev/null +++ b/platform/macos/msvs.py @@ -0,0 +1,11 @@ +# Tuples with the name of the arch +def get_platforms(): + return [("arm64", "arm64"), ("x64", "x86_64")] + + +def get_configurations(): + return ["editor", "template_debug", "template_release"] + + +def get_build_prefix(env): + return [] diff --git a/platform/windows/msvs.py b/platform/windows/msvs.py index 2d5ebe811a4..ff8a379867d 100644 --- a/platform/windows/msvs.py +++ b/platform/windows/msvs.py @@ -12,9 +12,13 @@ def get_configurations(): def get_build_prefix(env): + if not env.msvc: + return [] batch_file = methods.find_visual_c_batch_file(env) return [ + "cmd /V /C", "set "plat=$(PlatformTarget)"", - "(if "$(PlatformTarget)"=="x64" (set "plat=x86_amd64"))", - f"call "{batch_file}" !plat!", + "^& (if "$(PlatformTarget)"=="x64" (set "plat=x86_amd64"))", + f"^& call "{batch_file}" !plat!", + "^&", ]