Skip to content

Commit 5075d6b

Browse files
committed
Allow building on Windows using Clang without MinGW
1 parent f4af820 commit 5075d6b

File tree

5 files changed

+250
-5
lines changed

5 files changed

+250
-5
lines changed

SConstruct

+13-3
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ elif platform_arg == "web":
139139
custom_tools = ["cc", "c++", "ar", "link", "textfile", "zip"]
140140
elif os.name == "nt" and methods.get_cmdline_bool("use_mingw", False):
141141
custom_tools = ["mingw"]
142+
elif os.name == "nt" and methods.get_cmdline_bool("use_llvm", False):
143+
custom_tools = ["clang", "clangxx", "ar", "link"]
142144

143145
# We let SCons build its default ENV as it includes OS-specific things which we don't
144146
# want to have to pull in manually.
@@ -745,9 +747,17 @@ if env.msvc:
745747
env.Append(CCFLAGS=["/Od"])
746748
else:
747749
if env["debug_symbols"]:
748-
# Adding dwarf-4 explicitly makes stacktraces work with clang builds,
749-
# otherwise addr2line doesn't understand them
750-
env.Append(CCFLAGS=["-gdwarf-4"])
750+
if env["platform"] == "windows" and env["use_llvm"] and not env["use_dwarf"]:
751+
# On Windows when using LLVM the -gcodeview creates debug symbols compatible
752+
# with Microsoft debuggers.
753+
# Additionally -g must be sent to the linker to make it actually produce
754+
# a PDB file containing the debug symbols, otherwise they are discarded.
755+
env.Append(CCFLAGS=["-gcodeview"])
756+
env.Append(LINKFLAGS=["-g"])
757+
else:
758+
# Adding dwarf-4 explicitly makes stacktraces work with clang builds,
759+
# otherwise addr2line doesn't understand them
760+
env.Append(CCFLAGS=["-gdwarf-4"])
751761
if methods.using_emcc(env):
752762
# Emscripten only produces dwarf symbols when using "-g3".
753763
env.Append(CCFLAGS=["-g3"])

drivers/SCsub

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ SConscript("coreaudio/SCsub")
1616
SConscript("pulseaudio/SCsub")
1717
if env["platform"] == "windows":
1818
SConscript("wasapi/SCsub")
19-
if not env.msvc:
19+
if not env.msvc and (env["use_mingw"] or not env["use_llvm"]):
2020
SConscript("backtrace/SCsub")
2121
if env["xaudio2"]:
2222
if "xaudio2" not in supported:

platform/windows/SCsub

+3
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ if env["windows_subsystem"] == "gui":
7070
if env.msvc:
7171
env_wrap.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"])
7272
env_wrap.Append(LINKFLAGS=["version.lib"])
73+
elif not env["use_mingw"] and env["use_llvm"]:
74+
env_wrap.Append(LINKFLAGS=["-Wl,-subsystem:console"])
75+
env_wrap.Append(LIBS=["version"])
7376
else:
7477
env_wrap.Append(LINKFLAGS=["-Wl,--subsystem,console"])
7578
env_wrap.Append(LIBS=["version"])

platform/windows/crash_handler_windows.h

+2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@
3636

3737
// Crash handler exception only enabled with MSVC
3838
#if defined(DEBUG_ENABLED)
39+
#if !defined(__clang__)
3940
#define CRASH_HANDLER_EXCEPTION 1
41+
#endif
4042

4143
#ifdef _MSC_VER
4244
extern DWORD CrashHandlerException(EXCEPTION_POINTERS *ep);

platform/windows/detect.py

+231-1
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ def get_opts():
194194
),
195195
BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed.", False),
196196
BoolVariable("use_llvm", "Use the LLVM compiler", False),
197+
BoolVariable("use_dwarf", "Produce DWARF debug symbols instead of PDB when using the LLVM compiler", False),
197198
BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True),
198199
BoolVariable("use_asan", "Use address sanitizer (ASAN)", False),
199200
BoolVariable("use_ubsan", "Use LLVM compiler undefined behavior sanitizer (UBSAN)", False),
@@ -364,6 +365,34 @@ def setup_mingw(env: "SConsEnvironment"):
364365
print("Using MinGW, arch %s" % (env["arch"]))
365366

366367

368+
def setup_llvm(env):
369+
"""Set up env for use with clang"""
370+
371+
Version = False
372+
373+
try:
374+
out = subprocess.Popen(
375+
"clang --version",
376+
shell=True,
377+
encoding="utf-8",
378+
stderr=subprocess.PIPE,
379+
stdout=subprocess.PIPE,
380+
)
381+
outs, errs = out.communicate()
382+
if out.returncode == 0:
383+
import re
384+
385+
Version = re.search("^clang version ([0-9\\.]+)\n", outs).group(1)
386+
except Exception:
387+
pass
388+
389+
if not Version:
390+
print_error("CLang could not be found, make sure it is installed and in PATH.")
391+
sys.exit(255)
392+
393+
print("Found clang version %s, arch %s" % (Version, env["arch"]))
394+
395+
367396
def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
368397
"""Configure env to work with MSVC"""
369398

@@ -947,6 +976,201 @@ def configure_mingw(env: "SConsEnvironment"):
947976
env.Append(BUILDERS={"RES": env.Builder(action=build_res_file, suffix=".o", src_suffix=".rc")})
948977

949978

979+
def configure_llvm(env: "SConsEnvironment"):
980+
env.use_windows_spawn_fix()
981+
982+
if env["target"] != "template_release" and env.dev_build:
983+
# Allow big objects. It's supposed not to have drawbacks but seems to break
984+
# GCC LTO, so enabling for debug builds only (which are not built with LTO
985+
# and are the only ones with too big objects).
986+
env.Append(CCFLAGS=["-Wa,-mbig-obj"])
987+
988+
if env["windows_subsystem"] == "gui":
989+
env.Append(LINKFLAGS=["-Wl,-subsystem:windows"])
990+
else:
991+
env.Append(LINKFLAGS=["-Wl,-subsystem:console"])
992+
env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"])
993+
994+
## Compiler configuration
995+
996+
if os.name != "nt":
997+
env["PROGSUFFIX"] = env["PROGSUFFIX"] + ".exe" # for linux cross-compilation
998+
999+
if env["arch"] == "x86_32":
1000+
if env["use_static_cpp"]:
1001+
env.Append(LINKFLAGS=["-static"])
1002+
env.Append(LINKFLAGS=["-static-libgcc"])
1003+
env.Append(LINKFLAGS=["-static-libstdc++"])
1004+
else:
1005+
if env["use_static_cpp"]:
1006+
env.Append(LINKFLAGS=["-static"])
1007+
1008+
# FIXME: Temporarily disabled, because llvm doesn't like the assembly
1009+
# if env["arch"] in ["x86_32", "x86_64"]:
1010+
# env["x86_libtheora_opt_gcc"] = True
1011+
1012+
env.Append(CCFLAGS=["-ffp-contract=off"])
1013+
1014+
env["CC"] = "clang"
1015+
env["CXX"] = "clang++"
1016+
env["AS"] = "clang" # CLang can act as an assembler, so it is not usually bundled with llvm-as
1017+
env["AR"] = "llvm-ar"
1018+
env["RANLIB"] = "llvm-ranlib"
1019+
env.extra_suffix = ".llvm" + env.extra_suffix
1020+
1021+
## LTO
1022+
1023+
if env["lto"] == "auto": # Full LTO for production with LLVM.
1024+
env["lto"] = "full"
1025+
1026+
if env["lto"] != "none":
1027+
if env["lto"] == "thin":
1028+
env.Append(LINKFLAGS=["-flto=thin"])
1029+
else:
1030+
env.Append(LINKFLAGS=["-flto"])
1031+
1032+
if env["use_asan"]:
1033+
env.Append(LINKFLAGS=["-Wl,-stack:" + str(STACK_SIZE_SANITIZERS)])
1034+
else:
1035+
env.Append(LINKFLAGS=["-Wl,-stack:" + str(STACK_SIZE)])
1036+
1037+
# Sanitizers
1038+
1039+
if int(env["target_win_version"], 16) < 0x0601:
1040+
print_error("`target_win_version` should be 0x0601 or higher (Windows 7).")
1041+
sys.exit(255)
1042+
1043+
if env["use_asan"] or env["use_ubsan"]:
1044+
if env["arch"] not in ["x86_32", "x86_64"]:
1045+
print("Sanitizers are only supported for x86_32 and x86_64.")
1046+
sys.exit(255)
1047+
1048+
env.extra_suffix += ".san"
1049+
env.AppendUnique(CPPDEFINES=["SANITIZERS_ENABLED"])
1050+
san_flags = []
1051+
if env["use_asan"]:
1052+
san_flags.append("-fsanitize=address")
1053+
if env["use_ubsan"]:
1054+
san_flags.append("-fsanitize=undefined")
1055+
# Disable the vptr check since it gets triggered on any COM interface calls.
1056+
san_flags.append("-fno-sanitize=vptr")
1057+
env.Append(CFLAGS=san_flags)
1058+
env.Append(CCFLAGS=san_flags)
1059+
env.Append(LINKFLAGS=san_flags)
1060+
1061+
if os.name == "nt" and methods._colorize:
1062+
env.Append(CCFLAGS=["$(-fansi-escape-codes$)", "$(-fcolor-diagnostics$)"])
1063+
1064+
# FIXME: Using thin AR causes link to fail. Possible incompatibility with MSVC linker. Force use of lld instead?
1065+
# if get_is_ar_thin_supported(env):
1066+
# env.Append(ARFLAGS=["--thin"])
1067+
1068+
## Compile flags
1069+
1070+
env.Append(CPPDEFINES=[])
1071+
env.Append(
1072+
CPPDEFINES=[
1073+
"WINDOWS_ENABLED",
1074+
"WASAPI_ENABLED",
1075+
"WINMIDI_ENABLED",
1076+
"TYPED_METHOD_BIND",
1077+
"WIN32",
1078+
# Prevent Windows SDK headers from defining min and max macros,
1079+
# which conflict with min and max templates
1080+
"NOMINMAX",
1081+
# The inline assembly trick used for older MSVC versions does not work for LLVM.
1082+
# Fall back to portable implementation.
1083+
# An alternative is to define __MINGW32__ but this would influence other libraries
1084+
"R128_STDC_ONLY",
1085+
("WINVER", env["target_win_version"]),
1086+
("_WIN32_WINNT", env["target_win_version"]),
1087+
]
1088+
)
1089+
env.Append(
1090+
LIBS=[
1091+
"avrt",
1092+
"bcrypt",
1093+
"crypt32",
1094+
"dinput8",
1095+
"dsound",
1096+
"dwmapi",
1097+
"dwrite",
1098+
"dxguid",
1099+
"gdi32",
1100+
"imm32",
1101+
"iphlpapi",
1102+
"kernel32",
1103+
"ntdll",
1104+
"ole32",
1105+
"oleaut32",
1106+
"sapi",
1107+
"shell32",
1108+
"advapi32",
1109+
"shlwapi",
1110+
"user32",
1111+
"uuid",
1112+
"wbemuuid",
1113+
"winmm",
1114+
"ws2_32",
1115+
"wsock32",
1116+
]
1117+
)
1118+
1119+
if env.debug_features:
1120+
env.Append(LIBS=["psapi", "dbghelp"])
1121+
1122+
if env["vulkan"]:
1123+
env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
1124+
if not env["use_volk"]:
1125+
env.Append(LIBS=["vulkan"])
1126+
1127+
if env["d3d12"]:
1128+
# Check whether we have d3d12 dependencies installed.
1129+
if not os.path.exists(env["mesa_libs"]):
1130+
print_error(
1131+
"The Direct3D 12 rendering driver requires dependencies to be installed.\n"
1132+
"You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n"
1133+
"See the documentation for more information:\n\t"
1134+
"https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html"
1135+
)
1136+
sys.exit(255)
1137+
1138+
env.AppendUnique(CPPDEFINES=["D3D12_ENABLED", "RD_ENABLED"])
1139+
env.Append(LIBS=["dxgi", "dxguid"])
1140+
1141+
# PIX
1142+
if env["arch"] not in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]):
1143+
env["use_pix"] = False
1144+
1145+
if env["use_pix"]:
1146+
arch_subdir = "arm64" if env["arch"] == "arm64" else "x64"
1147+
1148+
env.Append(LIBPATH=[env["pix_path"] + "/bin/" + arch_subdir])
1149+
env.Append(LIBS=["WinPixEventRuntime"])
1150+
1151+
env.Append(LIBPATH=[env["mesa_libs"] + "/bin"])
1152+
env.Append(LIBS=["libNIR.windows." + env["arch"]])
1153+
env.Append(LIBS=["version"]) # Mesa dependency.
1154+
1155+
if env["opengl3"]:
1156+
env.Append(CPPDEFINES=["GLES3_ENABLED"])
1157+
if env["angle_libs"] != "":
1158+
env.AppendUnique(CPPDEFINES=["EGL_STATIC"])
1159+
env.Append(LIBPATH=[env["angle_libs"]])
1160+
env.Append(
1161+
LIBS=[
1162+
"EGL.windows." + env["arch"],
1163+
"GLES.windows." + env["arch"],
1164+
"ANGLE.windows." + env["arch"],
1165+
]
1166+
)
1167+
env.Append(LIBS=["dxgi", "d3d9", "d3d11"])
1168+
env.Prepend(CPPPATH=["#thirdparty/angle/include"])
1169+
1170+
# resrc
1171+
env.Append(BUILDERS={"RES": env.Builder(action=build_res_file, suffix=".o", src_suffix=".rc")})
1172+
1173+
9501174
def configure(env: "SConsEnvironment"):
9511175
# Validate arch.
9521176
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
@@ -965,7 +1189,10 @@ def configure(env: "SConsEnvironment"):
9651189
env["ENV"]["TMP"] = os.environ["TMP"]
9661190

9671191
# First figure out which compiler, version, and target arch we're using
968-
if os.getenv("VCINSTALLDIR") and detect_build_env_arch() and not env["use_mingw"]:
1192+
if env["use_llvm"] and not env["use_mingw"]:
1193+
setup_llvm(env)
1194+
env.msvc = False
1195+
elif os.getenv("VCINSTALLDIR") and detect_build_env_arch() and not env["use_mingw"]:
9691196
setup_msvc_manual(env)
9701197
env.msvc = True
9711198
vcvars_msvc_config = True
@@ -981,5 +1208,8 @@ def configure(env: "SConsEnvironment"):
9811208
if env.msvc:
9821209
configure_msvc(env, vcvars_msvc_config)
9831210

1211+
elif env["use_llvm"]:
1212+
configure_llvm(env)
1213+
9841214
else: # MinGW
9851215
configure_mingw(env)

0 commit comments

Comments
 (0)