mirror of
https://github.com/godotengine/godot.git
synced 2024-11-24 21:22:48 +00:00
Add new VS proj generation logic that supports any platform that wants to opt in
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` To generate build configuration files for all platforms+targets+arch combinations, users should call ``` scons vsproj=yes platform=XXX target=YYY [other build flags] ``` for each combination of platform+target[+arch]. This will generate the relevant vs project files but skip the build process, so that project files can be quickly generated without waiting for a command line build. This lets project files be quickly generated even if there are build errors. All possible combinations of platform+target are created in the solution file by default, but they won't do anything until each one is set up with a scons vsproj=yes command for the respective platform in the appropriate command line. This lets users only generate the combinations they need, and VS won't have to parse settings for other combos. 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 currently being processed by scons. 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 AND build from the command line, run ``` scons vsproj=yes vsproj_gen_only=no ```
This commit is contained in:
parent
9adb7c7d13
commit
7638a6c981
2
.github/workflows/windows_builds.yml
vendored
2
.github/workflows/windows_builds.yml
vendored
@ -28,7 +28,7 @@ jobs:
|
||||
target: editor
|
||||
tests: true
|
||||
# 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"
|
||||
|
||||
- name: Template (target=template_release)
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -367,3 +367,4 @@ $RECYCLE.BIN/
|
||||
*.msm
|
||||
*.msp
|
||||
*.lnk
|
||||
*.generated.props
|
||||
|
@ -1000,9 +1000,6 @@ if selected_platform in platform_list:
|
||||
|
||||
# Microsoft Visual Studio Project Generation
|
||||
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"]]
|
||||
methods.generate_vs_project(env, ARGUMENTS, env["vsproj_name"])
|
||||
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"]
|
||||
|
||||
|
||||
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):
|
||||
program = env.ProgramOriginal(program, sources, **args)
|
||||
env.Precious(program)
|
||||
@ -1229,3 +1074,456 @@ def dump(env):
|
||||
|
||||
with open(".scons_env.json", "w") as f:
|
||||
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()
|
||||
|
21
misc/msvs/props.template
Normal file
21
misc/msvs/props.template
Normal file
@ -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>
|
20
misc/msvs/sln.template
Normal file
20
misc/msvs/sln.template
Normal file
@ -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
|
30
misc/msvs/vcxproj.filters.template
Normal file
30
misc/msvs/vcxproj.filters.template
Normal file
@ -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%%
|
||||
-->
|
42
misc/msvs/vcxproj.template
Normal file
42
misc/msvs/vcxproj.template
Normal file
@ -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
|
||||
import platform_windows_builders
|
||||
|
||||
sources = []
|
||||
|
||||
common_win = [
|
||||
"godot_windows.cpp",
|
||||
"crash_handler_windows.cpp",
|
||||
@ -43,7 +45,8 @@ res_file = "godot_res.rc"
|
||||
res_target = "godot_res" + env["OBJSUFFIX"]
|
||||
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"])
|
||||
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"])
|
||||
arrange_program_clean(prog_wrap)
|
||||
env_wrap.Depends(prog_wrap, prog)
|
||||
sources += common_win_wrap + res_wrap_obj
|
||||
|
||||
# Microsoft Visual Studio Project Generation
|
||||
if env["vsproj"]:
|
||||
@ -134,3 +138,5 @@ if not os.getenv("VCINSTALLDIR"):
|
||||
env.AddPostAction(prog, run_in_subprocess(platform_windows_builders.make_debug_mingw))
|
||||
if env["windows_subsystem"] == "gui":
|
||||
env.AddPostAction(prog_wrap, run_in_subprocess(platform_windows_builders.make_debug_mingw))
|
||||
|
||||
env.platform_sources += sources
|
||||
|
20
platform/windows/msvs.py
Normal file
20
platform/windows/msvs.py
Normal file
@ -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
Block a user