Merge pull request #84885 from shana/vsproj-for-everyone
New VS proj generation logic that supports any platform that wants to opt in
This commit is contained in:
commit
bbccd95d22
|
@ -28,7 +28,7 @@ jobs:
|
||||||
target: editor
|
target: editor
|
||||||
tests: true
|
tests: true
|
||||||
# Skip debug symbols, they're way too big with MSVC.
|
# Skip debug symbols, they're way too big with MSVC.
|
||||||
sconsflags: debug_symbols=no vsproj=yes windows_subsystem=console
|
sconsflags: debug_symbols=no vsproj=yes vsproj_gen_only=no windows_subsystem=console
|
||||||
bin: "./bin/godot.windows.editor.x86_64.exe"
|
bin: "./bin/godot.windows.editor.x86_64.exe"
|
||||||
|
|
||||||
- name: Template (target=template_release)
|
- name: Template (target=template_release)
|
||||||
|
|
|
@ -367,3 +367,4 @@ $RECYCLE.BIN/
|
||||||
*.msm
|
*.msm
|
||||||
*.msp
|
*.msp
|
||||||
*.lnk
|
*.lnk
|
||||||
|
*.generated.props
|
||||||
|
|
|
@ -1000,9 +1000,6 @@ if selected_platform in platform_list:
|
||||||
|
|
||||||
# Microsoft Visual Studio Project Generation
|
# Microsoft Visual Studio Project Generation
|
||||||
if env["vsproj"]:
|
if env["vsproj"]:
|
||||||
if os.name != "nt":
|
|
||||||
print("Error: The `vsproj` option is only usable on Windows with Visual Studio.")
|
|
||||||
Exit(255)
|
|
||||||
env["CPPPATH"] = [Dir(path) for path in env["CPPPATH"]]
|
env["CPPPATH"] = [Dir(path) for path in env["CPPPATH"]]
|
||||||
methods.generate_vs_project(env, ARGUMENTS, env["vsproj_name"])
|
methods.generate_vs_project(env, ARGUMENTS, env["vsproj_name"])
|
||||||
methods.generate_cpp_hint_file("cpp.hint")
|
methods.generate_cpp_hint_file("cpp.hint")
|
||||||
|
|
608
methods.py
608
methods.py
|
@ -774,161 +774,6 @@ def add_to_vs_project(env, sources):
|
||||||
env.vs_srcs += [basename + ".cpp"]
|
env.vs_srcs += [basename + ".cpp"]
|
||||||
|
|
||||||
|
|
||||||
def generate_vs_project(env, original_args, project_name="godot"):
|
|
||||||
batch_file = find_visual_c_batch_file(env)
|
|
||||||
filtered_args = original_args.copy()
|
|
||||||
# Ignore the "vsproj" option to not regenerate the VS project on every build
|
|
||||||
filtered_args.pop("vsproj", None)
|
|
||||||
# The "platform" option is ignored because only the Windows platform is currently supported for VS projects
|
|
||||||
filtered_args.pop("platform", None)
|
|
||||||
# The "target" option is ignored due to the way how targets configuration is performed for VS projects (there is a separate project configuration for each target)
|
|
||||||
filtered_args.pop("target", None)
|
|
||||||
# The "progress" option is ignored as the current compilation progress indication doesn't work in VS
|
|
||||||
filtered_args.pop("progress", None)
|
|
||||||
|
|
||||||
if batch_file:
|
|
||||||
|
|
||||||
class ModuleConfigs(Mapping):
|
|
||||||
# This version information (Win32, x64, Debug, Release) 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 = ["x86_32", "x86_64"]
|
|
||||||
CONFIGURATIONS = ["editor", "template_release", "template_debug"]
|
|
||||||
DEV_SUFFIX = ".dev" if env["dev_build"] else ""
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def for_every_variant(value):
|
|
||||||
return [value for _ in range(len(ModuleConfigs.CONFIGURATIONS) * len(ModuleConfigs.PLATFORMS))]
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
shared_targets_array = []
|
|
||||||
self.names = []
|
|
||||||
self.arg_dict = {
|
|
||||||
"variant": [],
|
|
||||||
"runfile": shared_targets_array,
|
|
||||||
"buildtarget": shared_targets_array,
|
|
||||||
"cpppaths": [],
|
|
||||||
"cppdefines": [],
|
|
||||||
"cmdargs": [],
|
|
||||||
}
|
|
||||||
self.add_mode() # default
|
|
||||||
|
|
||||||
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}{ModuleConfigs.DEV_SUFFIX}{".double" if env["precision"] == "double" else ""}.{plat_id}{f".{name}" if name else ""}.exe'
|
|
||||||
for config in ModuleConfigs.CONFIGURATIONS
|
|
||||||
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(list(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"))',
|
|
||||||
'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",
|
|
||||||
]
|
|
||||||
|
|
||||||
for arg, value in filtered_args.items():
|
|
||||||
common_build_postfix.append(f"{arg}={value}")
|
|
||||||
|
|
||||||
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)
|
|
||||||
add_to_vs_project(env, env.main_sources)
|
|
||||||
add_to_vs_project(env, env.modules_sources)
|
|
||||||
add_to_vs_project(env, env.scene_sources)
|
|
||||||
add_to_vs_project(env, env.servers_sources)
|
|
||||||
if env["tests"]:
|
|
||||||
add_to_vs_project(env, env.tests_sources)
|
|
||||||
if env.editor_build:
|
|
||||||
add_to_vs_project(env, env.editor_sources)
|
|
||||||
|
|
||||||
for header in glob_recursive("**/*.h"):
|
|
||||||
env.vs_incs.append(str(header))
|
|
||||||
|
|
||||||
module_configs = ModuleConfigs()
|
|
||||||
|
|
||||||
if env.get("module_mono_enabled"):
|
|
||||||
mono_defines = [("GD_MONO_HOT_RELOAD",)] if env.editor_build else []
|
|
||||||
module_configs.add_mode(
|
|
||||||
"mono",
|
|
||||||
cli_args="module_mono_enabled=yes",
|
|
||||||
defines=mono_defines,
|
|
||||||
)
|
|
||||||
|
|
||||||
scons_cmd = "scons"
|
|
||||||
|
|
||||||
path_to_venv = os.getenv("VIRTUAL_ENV")
|
|
||||||
path_to_scons_exe = Path(str(path_to_venv)) / "Scripts" / "scons.exe"
|
|
||||||
if path_to_venv and path_to_scons_exe.exists():
|
|
||||||
scons_cmd = str(path_to_scons_exe)
|
|
||||||
|
|
||||||
env["MSVSBUILDCOM"] = module_configs.build_commandline(scons_cmd)
|
|
||||||
env["MSVSREBUILDCOM"] = module_configs.build_commandline(f"{scons_cmd} vsproj=yes")
|
|
||||||
env["MSVSCLEANCOM"] = module_configs.build_commandline(f"{scons_cmd} --clean")
|
|
||||||
if not env.get("MSVS"):
|
|
||||||
env["MSVS"]["PROJECTSUFFIX"] = ".vcxproj"
|
|
||||||
env["MSVS"]["SOLUTIONSUFFIX"] = ".sln"
|
|
||||||
env.MSVSProject(
|
|
||||||
target=["#" + project_name + env["MSVSPROJECTSUFFIX"]],
|
|
||||||
incs=env.vs_incs,
|
|
||||||
srcs=env.vs_srcs,
|
|
||||||
auto_build_solution=1,
|
|
||||||
**module_configs,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
print("Could not locate Visual Studio batch file to set up the build environment. Not generating VS project.")
|
|
||||||
|
|
||||||
|
|
||||||
def precious_program(env, program, sources, **args):
|
def precious_program(env, program, sources, **args):
|
||||||
program = env.ProgramOriginal(program, sources, **args)
|
program = env.ProgramOriginal(program, sources, **args)
|
||||||
env.Precious(program)
|
env.Precious(program)
|
||||||
|
@ -1229,3 +1074,456 @@ def dump(env):
|
||||||
|
|
||||||
with open(".scons_env.json", "w") as f:
|
with open(".scons_env.json", "w") as f:
|
||||||
dump(env.Dictionary(), f, indent=4, default=non_serializable)
|
dump(env.Dictionary(), f, indent=4, default=non_serializable)
|
||||||
|
|
||||||
|
|
||||||
|
# Custom Visual Studio project generation logic that supports any platform that has a msvs.py
|
||||||
|
# script, so Visual Studio can be used to run scons for any platform, with the right defines per target.
|
||||||
|
# Invoked with scons vsproj=yes
|
||||||
|
#
|
||||||
|
# Only platforms that opt in to vs proj generation by having a msvs.py file in the platform folder are included.
|
||||||
|
# Platforms with a msvs.py file will be added to the solution, but only the current active platform+target+arch
|
||||||
|
# will have a build configuration generated, because we only know what the right defines/includes/flags/etc are
|
||||||
|
# on the active build target.
|
||||||
|
#
|
||||||
|
# Platforms that don't support an editor target will have a dummy editor target that won't do anything on build,
|
||||||
|
# but will have the files and configuration for the windows editor target.
|
||||||
|
#
|
||||||
|
# To generate build configuration files for all platforms+targets+arch combinations, users can call
|
||||||
|
# scons vsproj=yes
|
||||||
|
# for each combination of platform+target+arch. This will generate the relevant vs project files but
|
||||||
|
# skip the build process. This lets project files be quickly generated even if there are build errors.
|
||||||
|
#
|
||||||
|
# To generate AND build from the command line:
|
||||||
|
# scons vsproj=yes vsproj_gen_only=yes
|
||||||
|
def generate_vs_project(env, original_args, project_name="godot"):
|
||||||
|
# Augmented glob_recursive that also fills the dirs argument with traversed directories that have content.
|
||||||
|
def glob_recursive_2(pattern, dirs, node="."):
|
||||||
|
from SCons import Node
|
||||||
|
from SCons.Script import Glob
|
||||||
|
|
||||||
|
results = []
|
||||||
|
for f in Glob(str(node) + "/*", source=True):
|
||||||
|
if type(f) is Node.FS.Dir:
|
||||||
|
results += glob_recursive_2(pattern, dirs, f)
|
||||||
|
r = Glob(str(node) + "/" + pattern, source=True)
|
||||||
|
if len(r) > 0 and not str(node) in dirs:
|
||||||
|
d = ""
|
||||||
|
for part in str(node).split("\\"):
|
||||||
|
d += part
|
||||||
|
if not d in dirs:
|
||||||
|
dirs.append(d)
|
||||||
|
d += "\\"
|
||||||
|
results += r
|
||||||
|
return results
|
||||||
|
|
||||||
|
def get_bool(args, option, default):
|
||||||
|
from SCons.Variables.BoolVariable import _text2bool
|
||||||
|
|
||||||
|
val = args.get(option, default)
|
||||||
|
if val is not None:
|
||||||
|
try:
|
||||||
|
return _text2bool(val)
|
||||||
|
except:
|
||||||
|
return default
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def format_key_value(v):
|
||||||
|
if type(v) in [tuple, list]:
|
||||||
|
return v[0] if len(v) == 1 else f"{v[0]}={v[1]}"
|
||||||
|
return v
|
||||||
|
|
||||||
|
filtered_args = original_args.copy()
|
||||||
|
|
||||||
|
# Ignore the "vsproj" option to not regenerate the VS project on every build
|
||||||
|
filtered_args.pop("vsproj", None)
|
||||||
|
|
||||||
|
# This flag allows users to regenerate the proj files but skip the building process.
|
||||||
|
# This lets projects be regenerated even if there are build errors.
|
||||||
|
filtered_args.pop("vsproj_gen_only", None)
|
||||||
|
|
||||||
|
# The "progress" option is ignored as the current compilation progress indication doesn't work in VS
|
||||||
|
filtered_args.pop("progress", None)
|
||||||
|
|
||||||
|
# We add these three manually because they might not be explicitly passed in, and it's important to always set them.
|
||||||
|
filtered_args.pop("platform", None)
|
||||||
|
filtered_args.pop("target", None)
|
||||||
|
filtered_args.pop("arch", None)
|
||||||
|
|
||||||
|
platform = env["platform"]
|
||||||
|
target = env["target"]
|
||||||
|
arch = env["arch"]
|
||||||
|
|
||||||
|
vs_configuration = {}
|
||||||
|
common_build_prefix = []
|
||||||
|
confs = []
|
||||||
|
for x in sorted(glob.glob("platform/*")):
|
||||||
|
# Only platforms that opt in to vs proj generation are included.
|
||||||
|
if not os.path.isdir(x) or not os.path.exists(x + "/msvs.py"):
|
||||||
|
continue
|
||||||
|
tmppath = "./" + x
|
||||||
|
sys.path.insert(0, tmppath)
|
||||||
|
import msvs
|
||||||
|
|
||||||
|
vs_plats = []
|
||||||
|
vs_confs = []
|
||||||
|
try:
|
||||||
|
platform_name = x[9:]
|
||||||
|
vs_plats = msvs.get_platforms()
|
||||||
|
vs_confs = msvs.get_configurations()
|
||||||
|
val = []
|
||||||
|
for plat in vs_plats:
|
||||||
|
val += [{"platform": plat[0], "architecture": plat[1]}]
|
||||||
|
|
||||||
|
vsconf = {"platform": platform_name, "targets": vs_confs, "arches": val}
|
||||||
|
confs += [vsconf]
|
||||||
|
|
||||||
|
# Save additional information about the configuration for the actively selected platform,
|
||||||
|
# so we can generate the platform-specific props file with all the build commands/defines/etc
|
||||||
|
if platform == platform_name:
|
||||||
|
common_build_prefix = msvs.get_build_prefix(env)
|
||||||
|
vs_configuration = vsconf
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
sys.path.remove(tmppath)
|
||||||
|
sys.modules.pop("msvs")
|
||||||
|
|
||||||
|
headers = []
|
||||||
|
headers_dirs = []
|
||||||
|
for file in glob_recursive_2("*.h", headers_dirs):
|
||||||
|
headers.append(str(file).replace("/", "\\"))
|
||||||
|
for file in glob_recursive_2("*.hpp", headers_dirs):
|
||||||
|
headers.append(str(file).replace("/", "\\"))
|
||||||
|
|
||||||
|
sources = []
|
||||||
|
sources_dirs = []
|
||||||
|
for file in glob_recursive_2("*.cpp", sources_dirs):
|
||||||
|
sources.append(str(file).replace("/", "\\"))
|
||||||
|
for file in glob_recursive_2("*.c", sources_dirs):
|
||||||
|
sources.append(str(file).replace("/", "\\"))
|
||||||
|
|
||||||
|
others = []
|
||||||
|
others_dirs = []
|
||||||
|
for file in glob_recursive_2("*.natvis", others_dirs):
|
||||||
|
others.append(str(file).replace("/", "\\"))
|
||||||
|
for file in glob_recursive_2("*.glsl", others_dirs):
|
||||||
|
others.append(str(file).replace("/", "\\"))
|
||||||
|
|
||||||
|
skip_filters = False
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
|
||||||
|
md5 = hashlib.md5(
|
||||||
|
json.dumps(headers + headers_dirs + sources + sources_dirs + others + others_dirs, sort_keys=True).encode(
|
||||||
|
"utf-8"
|
||||||
|
)
|
||||||
|
).hexdigest()
|
||||||
|
|
||||||
|
if os.path.exists(f"{project_name}.vcxproj.filters"):
|
||||||
|
existing_filters = open(f"{project_name}.vcxproj.filters", "r").read()
|
||||||
|
match = re.search(r"(?ms)^<!-- CHECKSUM$.([0-9a-f]{32})", existing_filters)
|
||||||
|
if match is not None and md5 == match.group(1):
|
||||||
|
skip_filters = True
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
# Don't regenerate the filters file if nothing has changed, so we keep the existing UUIDs.
|
||||||
|
if not skip_filters:
|
||||||
|
print(f"Regenerating {project_name}.vcxproj.filters")
|
||||||
|
|
||||||
|
filters_template = open("misc/msvs/vcxproj.filters.template", "r").read()
|
||||||
|
for i in range(1, 10):
|
||||||
|
filters_template = filters_template.replace(f"%%UUID{i}%%", str(uuid.uuid4()))
|
||||||
|
|
||||||
|
filters = ""
|
||||||
|
|
||||||
|
for d in headers_dirs:
|
||||||
|
filters += f'<Filter Include="Header Files\\{d}"><UniqueIdentifier>{{{str(uuid.uuid4())}}}</UniqueIdentifier></Filter>\n'
|
||||||
|
for d in sources_dirs:
|
||||||
|
filters += f'<Filter Include="Source Files\\{d}"><UniqueIdentifier>{{{str(uuid.uuid4())}}}</UniqueIdentifier></Filter>\n'
|
||||||
|
for d in others_dirs:
|
||||||
|
filters += f'<Filter Include="Other Files\\{d}"><UniqueIdentifier>{{{str(uuid.uuid4())}}}</UniqueIdentifier></Filter>\n'
|
||||||
|
|
||||||
|
filters_template = filters_template.replace("%%FILTERS%%", filters)
|
||||||
|
|
||||||
|
filters = ""
|
||||||
|
for file in headers:
|
||||||
|
filters += (
|
||||||
|
f'<ClInclude Include="{file}"><Filter>Header Files\\{os.path.dirname(file)}</Filter></ClInclude>\n'
|
||||||
|
)
|
||||||
|
filters_template = filters_template.replace("%%INCLUDES%%", filters)
|
||||||
|
|
||||||
|
filters = ""
|
||||||
|
for file in sources:
|
||||||
|
filters += (
|
||||||
|
f'<ClCompile Include="{file}"><Filter>Source Files\\{os.path.dirname(file)}</Filter></ClCompile>\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
filters_template = filters_template.replace("%%COMPILES%%", filters)
|
||||||
|
|
||||||
|
filters = ""
|
||||||
|
for file in others:
|
||||||
|
filters += f'<None Include="{file}"><Filter>Other Files\\{os.path.dirname(file)}</Filter></None>\n'
|
||||||
|
filters_template = filters_template.replace("%%OTHERS%%", filters)
|
||||||
|
|
||||||
|
filters_template = filters_template.replace("%%HASH%%", md5)
|
||||||
|
|
||||||
|
with open(f"{project_name}.vcxproj.filters", "w") as f:
|
||||||
|
f.write(filters_template)
|
||||||
|
|
||||||
|
envsources = []
|
||||||
|
|
||||||
|
envsources += env.core_sources
|
||||||
|
envsources += env.drivers_sources
|
||||||
|
envsources += env.main_sources
|
||||||
|
envsources += env.modules_sources
|
||||||
|
envsources += env.scene_sources
|
||||||
|
envsources += env.servers_sources
|
||||||
|
if env.editor_build:
|
||||||
|
envsources += env.editor_sources
|
||||||
|
envsources += env.platform_sources
|
||||||
|
|
||||||
|
headers_active = []
|
||||||
|
sources_active = []
|
||||||
|
others_active = []
|
||||||
|
for x in envsources:
|
||||||
|
fname = ""
|
||||||
|
if type(x) == type(""):
|
||||||
|
fname = env.File(x).path
|
||||||
|
else:
|
||||||
|
# Some object files might get added directly as a File object and not a list.
|
||||||
|
try:
|
||||||
|
fname = env.File(x)[0].path
|
||||||
|
except:
|
||||||
|
fname = x.path
|
||||||
|
pass
|
||||||
|
|
||||||
|
if fname:
|
||||||
|
fname = fname.replace("\\\\", "/")
|
||||||
|
parts = os.path.splitext(fname)
|
||||||
|
basename = parts[0]
|
||||||
|
ext = parts[1]
|
||||||
|
idx = fname.find(env["OBJSUFFIX"])
|
||||||
|
if ext in [".h", ".hpp"]:
|
||||||
|
headers_active += [fname]
|
||||||
|
elif ext in [".c", ".cpp"]:
|
||||||
|
sources_active += [fname]
|
||||||
|
elif idx > 0:
|
||||||
|
basename = fname[:idx]
|
||||||
|
if os.path.isfile(basename + ".h"):
|
||||||
|
headers_active += [basename + ".h"]
|
||||||
|
elif os.path.isfile(basename + ".hpp"):
|
||||||
|
headers_active += [basename + ".hpp"]
|
||||||
|
elif basename.endswith(".gen") and os.path.isfile(basename[:-4] + ".h"):
|
||||||
|
headers_active += [basename[:-4] + ".h"]
|
||||||
|
if os.path.isfile(basename + ".c"):
|
||||||
|
sources_active += [basename + ".c"]
|
||||||
|
elif os.path.isfile(basename + ".cpp"):
|
||||||
|
sources_active += [basename + ".cpp"]
|
||||||
|
else:
|
||||||
|
fname = os.path.relpath(os.path.abspath(fname), env.Dir("").abspath)
|
||||||
|
others_active += [fname]
|
||||||
|
|
||||||
|
all_items = []
|
||||||
|
properties = []
|
||||||
|
activeItems = []
|
||||||
|
extraItems = []
|
||||||
|
|
||||||
|
set_headers = set(headers_active)
|
||||||
|
set_sources = set(sources_active)
|
||||||
|
set_others = set(others_active)
|
||||||
|
for file in headers:
|
||||||
|
all_items.append(f'<ClInclude Include="{file}">')
|
||||||
|
all_items.append(
|
||||||
|
f" <ExcludedFromBuild Condition=\"!$(ActiveProjectItemList.Contains(';{file};'))\">true</ExcludedFromBuild>"
|
||||||
|
)
|
||||||
|
all_items.append("</ClInclude>")
|
||||||
|
if file in set_headers:
|
||||||
|
activeItems.append(file)
|
||||||
|
|
||||||
|
for file in sources:
|
||||||
|
all_items.append(f'<ClCompile Include="{file}">')
|
||||||
|
all_items.append(
|
||||||
|
f" <ExcludedFromBuild Condition=\"!$(ActiveProjectItemList.Contains(';{file};'))\">true</ExcludedFromBuild>"
|
||||||
|
)
|
||||||
|
all_items.append("</ClCompile>")
|
||||||
|
if file in set_sources:
|
||||||
|
activeItems.append(file)
|
||||||
|
|
||||||
|
for file in others:
|
||||||
|
all_items.append(f'<None Include="{file}">')
|
||||||
|
all_items.append(
|
||||||
|
f" <ExcludedFromBuild Condition=\"!$(ActiveProjectItemList.Contains(';{file};'))\">true</ExcludedFromBuild>"
|
||||||
|
)
|
||||||
|
all_items.append("</None>")
|
||||||
|
if file in set_others:
|
||||||
|
activeItems.append(file)
|
||||||
|
|
||||||
|
if vs_configuration:
|
||||||
|
vsconf = ""
|
||||||
|
for a in vs_configuration["arches"]:
|
||||||
|
if arch == a["architecture"]:
|
||||||
|
vsconf = f'{target}|{a["platform"]}'
|
||||||
|
break
|
||||||
|
|
||||||
|
condition = "'$(Configuration)|$(Platform)'=='" + vsconf + "'"
|
||||||
|
properties.append("<ActiveProjectItemList>;" + ";".join(activeItems) + ";</ActiveProjectItemList>")
|
||||||
|
output = f'bin\\godot{env["PROGSUFFIX"]}'
|
||||||
|
|
||||||
|
props_template = open("misc/msvs/props.template", "r").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))
|
||||||
|
|
||||||
|
props_template = props_template.replace("%%OUTPUT%%", output)
|
||||||
|
|
||||||
|
props_template = props_template.replace(
|
||||||
|
"%%DEFINES%%", ";".join([format_key_value(v) for v in list(env["CPPDEFINES"])])
|
||||||
|
)
|
||||||
|
props_template = props_template.replace("%%INCLUDES%%", ";".join([str(j) for j in env["CPPPATH"]]))
|
||||||
|
props_template = props_template.replace(
|
||||||
|
"%%OPTIONS%%",
|
||||||
|
" ".join(env["CCFLAGS"]) + " " + " ".join([x for x in env["CXXFLAGS"] if not x.startswith("$")]),
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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('\\'))"",
|
||||||
|
"progress=no",
|
||||||
|
f"platform={platform}",
|
||||||
|
f"target={target}",
|
||||||
|
f"arch={arch}",
|
||||||
|
]
|
||||||
|
|
||||||
|
for arg, value in filtered_args.items():
|
||||||
|
common_build_postfix.append(f"{arg}={value}")
|
||||||
|
|
||||||
|
cmd_rebuild = [
|
||||||
|
"vsproj=yes",
|
||||||
|
f"vsproj_name={project_name}",
|
||||||
|
] + common_build_postfix
|
||||||
|
|
||||||
|
cmd_clean = [
|
||||||
|
"--clean",
|
||||||
|
] + common_build_postfix
|
||||||
|
|
||||||
|
commands = "scons"
|
||||||
|
if len(common_build_prefix) == 0:
|
||||||
|
commands = "echo Starting SCons && cmd /V /C " + commands
|
||||||
|
else:
|
||||||
|
common_build_prefix[0] = "echo Starting SCons && cmd /V /C " + common_build_prefix[0]
|
||||||
|
|
||||||
|
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)])
|
||||||
|
props_template = props_template.replace("%%REBUILD%%", cmd)
|
||||||
|
|
||||||
|
cmd = " ^& ".join(common_build_prefix + [" ".join([commands] + cmd_clean)])
|
||||||
|
props_template = props_template.replace("%%CLEAN%%", cmd)
|
||||||
|
|
||||||
|
with open(f"{project_name}.{platform}.{target}.{arch}.generated.props", "w") as f:
|
||||||
|
f.write(props_template)
|
||||||
|
|
||||||
|
proj_uuid = str(uuid.uuid4())
|
||||||
|
sln_uuid = str(uuid.uuid4())
|
||||||
|
|
||||||
|
if os.path.exists(f"{project_name}.sln"):
|
||||||
|
for line in open(f"{project_name}.sln", "r").read().splitlines():
|
||||||
|
if line.startswith('Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}")'):
|
||||||
|
proj_uuid = re.search(
|
||||||
|
r"\"{(\b[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-\b[0-9a-fA-F]{12}\b)}\"$",
|
||||||
|
line,
|
||||||
|
).group(1)
|
||||||
|
elif line.strip().startswith("SolutionGuid ="):
|
||||||
|
sln_uuid = re.search(
|
||||||
|
r"{(\b[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-\b[0-9a-fA-F]{12}\b)}", line
|
||||||
|
).group(1)
|
||||||
|
break
|
||||||
|
|
||||||
|
configurations = []
|
||||||
|
imports = []
|
||||||
|
properties = []
|
||||||
|
section1 = []
|
||||||
|
section2 = []
|
||||||
|
for conf in confs:
|
||||||
|
godot_platform = conf["platform"]
|
||||||
|
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.
|
||||||
|
# These configurations are disabled, so they show up but won't build.
|
||||||
|
if godot_platform != "windows":
|
||||||
|
section1 += [f"editor|{sln_plat} = editor|{proj_plat}"]
|
||||||
|
section2 += [
|
||||||
|
f"{{{proj_uuid}}}.editor|{proj_plat}.ActiveCfg = editor|{proj_plat}",
|
||||||
|
]
|
||||||
|
|
||||||
|
for t in conf["targets"]:
|
||||||
|
godot_target = t
|
||||||
|
|
||||||
|
# Windows x86 is a special little flower that requires a project platform == Win32 but a solution platform == x86.
|
||||||
|
if godot_platform == "windows" and godot_target == "editor" and godot_arch == "x86_32":
|
||||||
|
sln_plat = "x86"
|
||||||
|
|
||||||
|
configurations += [
|
||||||
|
f'<ProjectConfiguration Include="{godot_target}|{proj_plat}">',
|
||||||
|
f" <Configuration>{godot_target}</Configuration>",
|
||||||
|
f" <Platform>{proj_plat}</Platform>",
|
||||||
|
"</ProjectConfiguration>",
|
||||||
|
]
|
||||||
|
|
||||||
|
if godot_platform != "windows":
|
||||||
|
configurations += [
|
||||||
|
f'<ProjectConfiguration Include="editor|{proj_plat}">',
|
||||||
|
f" <Configuration>editor</Configuration>",
|
||||||
|
f" <Platform>{proj_plat}</Platform>",
|
||||||
|
"</ProjectConfiguration>",
|
||||||
|
]
|
||||||
|
|
||||||
|
p = f"{project_name}.{godot_platform}.{godot_target}.{godot_arch}.generated.props"
|
||||||
|
imports += [
|
||||||
|
f'<Import Project="$(MSBuildProjectDirectory)\\{p}" Condition="Exists(\'$(MSBuildProjectDirectory)\\{p}\')"/>'
|
||||||
|
]
|
||||||
|
|
||||||
|
section1 += [f"{godot_target}|{sln_plat} = {godot_target}|{sln_plat}"]
|
||||||
|
|
||||||
|
section2 += [
|
||||||
|
f"{{{proj_uuid}}}.{godot_target}|{sln_plat}.ActiveCfg = {godot_target}|{proj_plat}",
|
||||||
|
f"{{{proj_uuid}}}.{godot_target}|{sln_plat}.Build.0 = {godot_target}|{proj_plat}",
|
||||||
|
]
|
||||||
|
|
||||||
|
section1 = sorted(section1)
|
||||||
|
section2 = sorted(section2)
|
||||||
|
|
||||||
|
proj_template = open("misc/msvs/vcxproj.template", "r").read()
|
||||||
|
|
||||||
|
proj_template = proj_template.replace("%%UUID%%", proj_uuid)
|
||||||
|
proj_template = proj_template.replace("%%CONFS%%", "\n ".join(configurations))
|
||||||
|
proj_template = proj_template.replace("%%IMPORTS%%", "\n ".join(imports))
|
||||||
|
proj_template = proj_template.replace("%%DEFAULT_ITEMS%%", "\n ".join(all_items))
|
||||||
|
proj_template = proj_template.replace("%%PROPERTIES%%", "\n ".join(properties))
|
||||||
|
|
||||||
|
with open(f"{project_name}.vcxproj", "w") as f:
|
||||||
|
f.write(proj_template)
|
||||||
|
|
||||||
|
sln_template = open("misc/msvs/sln.template", "r").read()
|
||||||
|
sln_template = sln_template.replace("%%NAME%%", project_name)
|
||||||
|
sln_template = sln_template.replace("%%UUID%%", proj_uuid)
|
||||||
|
sln_template = sln_template.replace("%%SLNUUID%%", sln_uuid)
|
||||||
|
sln_template = sln_template.replace("%%SECTION1%%", "\n ".join(section1))
|
||||||
|
sln_template = sln_template.replace("%%SECTION2%%", "\n ".join(section2))
|
||||||
|
with open(f"{project_name}.sln", "w") as f:
|
||||||
|
f.write(sln_template)
|
||||||
|
|
||||||
|
if get_bool(original_args, "vsproj_gen_only", True):
|
||||||
|
sys.exit()
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="17.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='%%VSCONF%%'">
|
||||||
|
<NMakeBuildCommandLine>%%BUILD%%</NMakeBuildCommandLine>
|
||||||
|
<NMakeReBuildCommandLine>%%REBUILD%%</NMakeReBuildCommandLine>
|
||||||
|
<NMakeCleanCommandLine>%%CLEAN%%</NMakeCleanCommandLine>
|
||||||
|
<NMakeOutput>%%OUTPUT%%</NMakeOutput>
|
||||||
|
<NMakePreprocessorDefinitions>%%DEFINES%%</NMakePreprocessorDefinitions>
|
||||||
|
<NMakeIncludeSearchPath>%%INCLUDES%%</NMakeIncludeSearchPath>
|
||||||
|
<NMakeForcedIncludes>$(NMakeForcedIncludes)</NMakeForcedIncludes>
|
||||||
|
<NMakeAssemblySearchPath>$(NMakeAssemblySearchPath)</NMakeAssemblySearchPath>
|
||||||
|
<NMakeForcedUsingAssemblies>$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies>
|
||||||
|
<AdditionalOptions>%%OPTIONS%%</AdditionalOptions>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="%%CONDITION%%">
|
||||||
|
%%PROPERTIES%%
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup Condition="%%CONDITION%%">
|
||||||
|
%%EXTRA_ITEMS%%
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,20 @@
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.7.34221.43
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "%%NAME%%", "%%NAME%%.vcxproj", "{%%UUID%%}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
%%SECTION1%%
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
%%SECTION2%%
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {%%SLNUUID%%}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="17.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Source Files">
|
||||||
|
<UniqueIdentifier>%%UUID1%%</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Header Files">
|
||||||
|
<UniqueIdentifier>%%UUID2%%</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Resource Files">
|
||||||
|
<UniqueIdentifier>%%UUID3%%</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Scripts">
|
||||||
|
<UniqueIdentifier>%%UUID4%%</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
%%FILTERS%%
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
%%COMPILES%%
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
%%INCLUDES%%
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
%%OTHERS%%
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
<!-- CHECKSUM
|
||||||
|
%%HASH%%
|
||||||
|
-->
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
%%CONFS%%
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>{%%UUID%%}</ProjectGuid>
|
||||||
|
<RootNamespace>godot</RootNamespace>
|
||||||
|
<Keyword>MakeFileProj</Keyword>
|
||||||
|
<VCProjectUpgraderObjectName>NoUpgrade</VCProjectUpgraderObjectName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
%%PROPERTIES%%
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Label="Configuration">
|
||||||
|
<ConfigurationType>Makefile</ConfigurationType>
|
||||||
|
<UseOfMfc>false</UseOfMfc>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<OutDir>$(SolutionDir)\bin\$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
<IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
|
||||||
|
<LayoutDir>$(OutDir)\Layout</LayoutDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
|
||||||
|
<ActiveProjectItemList></ActiveProjectItemList>
|
||||||
|
</PropertyGroup>
|
||||||
|
%%IMPORTS%%
|
||||||
|
<ItemGroup Condition="'$(IncludeListImported)'==''">
|
||||||
|
%%DEFAULT_ITEMS%%
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
|
@ -7,6 +7,8 @@ from pathlib import Path
|
||||||
from platform_methods import run_in_subprocess
|
from platform_methods import run_in_subprocess
|
||||||
import platform_windows_builders
|
import platform_windows_builders
|
||||||
|
|
||||||
|
sources = []
|
||||||
|
|
||||||
common_win = [
|
common_win = [
|
||||||
"godot_windows.cpp",
|
"godot_windows.cpp",
|
||||||
"crash_handler_windows.cpp",
|
"crash_handler_windows.cpp",
|
||||||
|
@ -43,7 +45,8 @@ res_file = "godot_res.rc"
|
||||||
res_target = "godot_res" + env["OBJSUFFIX"]
|
res_target = "godot_res" + env["OBJSUFFIX"]
|
||||||
res_obj = env.RES(res_target, res_file)
|
res_obj = env.RES(res_target, res_file)
|
||||||
|
|
||||||
sources = common_win + res_obj
|
env.add_source_files(sources, common_win)
|
||||||
|
sources += res_obj
|
||||||
|
|
||||||
prog = env.add_program("#bin/godot", sources, PROGSUFFIX=env["PROGSUFFIX"])
|
prog = env.add_program("#bin/godot", sources, PROGSUFFIX=env["PROGSUFFIX"])
|
||||||
arrange_program_clean(prog)
|
arrange_program_clean(prog)
|
||||||
|
@ -65,6 +68,7 @@ if env["windows_subsystem"] == "gui":
|
||||||
prog_wrap = env_wrap.add_program("#bin/godot", common_win_wrap + res_wrap_obj, PROGSUFFIX=env["PROGSUFFIX_WRAP"])
|
prog_wrap = env_wrap.add_program("#bin/godot", common_win_wrap + res_wrap_obj, PROGSUFFIX=env["PROGSUFFIX_WRAP"])
|
||||||
arrange_program_clean(prog_wrap)
|
arrange_program_clean(prog_wrap)
|
||||||
env_wrap.Depends(prog_wrap, prog)
|
env_wrap.Depends(prog_wrap, prog)
|
||||||
|
sources += common_win_wrap + res_wrap_obj
|
||||||
|
|
||||||
# Microsoft Visual Studio Project Generation
|
# Microsoft Visual Studio Project Generation
|
||||||
if env["vsproj"]:
|
if env["vsproj"]:
|
||||||
|
@ -134,3 +138,5 @@ if not os.getenv("VCINSTALLDIR"):
|
||||||
env.AddPostAction(prog, run_in_subprocess(platform_windows_builders.make_debug_mingw))
|
env.AddPostAction(prog, run_in_subprocess(platform_windows_builders.make_debug_mingw))
|
||||||
if env["windows_subsystem"] == "gui":
|
if env["windows_subsystem"] == "gui":
|
||||||
env.AddPostAction(prog_wrap, run_in_subprocess(platform_windows_builders.make_debug_mingw))
|
env.AddPostAction(prog_wrap, run_in_subprocess(platform_windows_builders.make_debug_mingw))
|
||||||
|
|
||||||
|
env.platform_sources += sources
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import methods
|
||||||
|
|
||||||
|
|
||||||
|
# Tuples with the name of the arch that will be used in VS, mapped to our internal arch names.
|
||||||
|
# For Windows platforms, Win32 is what VS wants. For other platforms, it can be different.
|
||||||
|
def get_platforms():
|
||||||
|
return [("Win32", "x86_32"), ("x64", "x86_64")]
|
||||||
|
|
||||||
|
|
||||||
|
def get_configurations():
|
||||||
|
return ["editor", "template_debug", "template_release"]
|
||||||
|
|
||||||
|
|
||||||
|
def get_build_prefix(env):
|
||||||
|
batch_file = methods.find_visual_c_batch_file(env)
|
||||||
|
return [
|
||||||
|
"set "plat=$(PlatformTarget)"",
|
||||||
|
"(if "$(PlatformTarget)"=="x64" (set "plat=x86_amd64"))",
|
||||||
|
f"call "{batch_file}" !plat!",
|
||||||
|
]
|
Loading…
Reference in New Issue