CollisionHook/AMBuildScript
2025-10-22 21:58:07 -05:00

639 lines
24 KiB
Python

# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
import os, sys
# Simple extensions do not need to modify this file.
class SDK(object):
def __init__(self, sdk, ext, aDef, name, platform, dir):
self.folder = "hl2sdk-" + dir
self.envvar = sdk
self.ext = ext
self.code = aDef
self.define = name
self.platform = platform
self.name = dir
self.path = None # Actual path
self.platformSpec = platform
# By default, nothing supports x64.
if type(platform) is list:
self.platformSpec = {p: ["x86"] for p in platform}
else:
self.platformSpec = platform
def shouldBuild(self, targets):
for cxx in targets:
if cxx.target.platform in self.platformSpec:
if cxx.target.arch in self.platformSpec[cxx.target.platform]:
return True
return False
WinLinux = ["windows", "linux"]
TF2 = {"windows": ["x86", "x86_64"], "linux": ["x86", "x86_64"]}
CSGO = {"windows": ["x86"], "linux": ["x86", "x86_64"]}
PossibleSDKs = {
# 'episode1': SDK('HL2SDK', '1.ep1', '1', 'EPISODEONE', WinLinux, 'episode1'),
# 'darkm': SDK('HL2SDK-DARKM', '2.darkm', '2', 'DARKMESSIAH', WinOnly, 'darkm'),
# 'orangebox': SDK('HL2SDKOB', '2.ep2', '3', 'ORANGEBOX', WinLinux, 'orangebox'),
# 'bgt': SDK('HL2SDK-BGT', '2.bgt', '4', 'BLOODYGOODTIME', WinOnly, 'bgt'),
# 'eye': SDK('HL2SDK-EYE', '2.eye', '5', 'EYE', WinOnly, 'eye'),
"css": SDK("HL2SDKCSS", "2.css", "6", "CSS", WinLinux, "css"),
"hl2dm": SDK("HL2SDKHL2DM", "2.hl2dm", "7", "HL2DM", WinLinux, "hl2dm"),
"dods": SDK("HL2SDKDODS", "2.dods", "8", "DODS", WinLinux, "dods"),
"sdk2013": SDK("HL2SDK2013", "2.sdk2013", "9", "SDK2013", WinLinux, "sdk2013"),
# 'bms': SDK('HL2SDKBMS', '2.bms', '11', 'BMS', WinLinux, 'bms'),
"tf2": SDK("HL2SDKTF2", "2.tf2", "12", "TF2", TF2, "tf2"),
"l4d": SDK("HL2SDKL4D", "2.l4d", "13", "LEFT4DEAD", WinLinux, "l4d"),
# 'nucleardawn': SDK('HL2SDKND', '2.nd', '14', 'NUCLEARDAWN', WinLinuxMac, 'nucleardawn'),
# 'contagion': SDK('HL2SDKCONTAGION', '2.contagion', '15', 'CONTAGION', WinOnly, 'contagion'),
"l4d2": SDK("HL2SDKL4D2", "2.l4d2", "16", "LEFT4DEAD2", WinLinux, "l4d2"),
# 'swarm': SDK('HL2SDK-SWARM', '2.swarm', '17', 'ALIENSWARM', WinOnly, 'swarm'),
# 'portal2': SDK('HL2SDKPORTAL2', '2.portal2', '18', 'PORTAL2', [], 'portal2'),
# 'insurgency': SDK('HL2SDKINSURGENCY', '2.insurgency', '19', 'INSURGENCY', WinLinuxMac, 'insurgency'),
# 'blade': SDK('HL2SDKBLADE', '2.blade', '21', 'BLADE', WinLinux, 'blade'),
"csgo": SDK("HL2SDKCSGO", "2.csgo", "23", "CSGO", CSGO, "csgo"),
}
def ResolveEnvPath(env, folder):
if env in os.environ:
path = os.environ[env]
if os.path.isdir(path):
return path
return None
head = os.getcwd()
oldhead = None
while head != None and head != oldhead:
path = os.path.join(head, folder)
if os.path.isdir(path):
return path
oldhead = head
head, tail = os.path.split(head)
return None
def Normalize(path):
return os.path.abspath(os.path.normpath(path))
def SetArchFlags(compiler):
if compiler.behavior == "gcc":
if compiler.target.arch == "x86_64":
compiler.cflags += ["-fPIC"]
elif compiler.like("msvc"):
if compiler.target.arch == "x86_64":
compiler.defines += ["WIN64"]
class ExtensionConfig(object):
def __init__(self):
self.sdks = {}
self.binaries = []
self.extensions = []
self.generated_headers = None
self.productVersion = None
self.mms_root = None
self.sm_root = None
self.all_targets = []
self.target_archs = set()
if builder.options.targets:
target_archs = builder.options.targets.split(",")
else:
target_archs = ["x86", "x86_64"]
for arch in target_archs:
try:
cxx = builder.DetectCxx(target_arch=arch)
self.target_archs.add(cxx.target.arch)
except Exception as e:
# Error if archs were manually overridden.
if builder.options.targets:
raise
print("Skipping target {}: {}".format(arch, e))
continue
self.all_targets.append(cxx)
if not self.all_targets:
raise Exception("No suitable C/C++ compiler was found.")
def use_auto_versioning(self):
return not getattr(builder.options, "disable_auto_versioning", False)
def AddVersioning(self, binary):
if binary.compiler.target.platform == "windows":
binary.sources += ["version.rc"]
binary.compiler.rcdefines += [
'BINARY_NAME="{0}"'.format(binary.outputFile),
"RC_COMPILE",
]
elif binary.compiler.target.platform == "mac":
if binary.type == "library":
binary.compiler.postlink += [
"-compatibility_version",
"1.0.0",
"-current_version",
self.productVersion,
]
if self.use_auto_versioning():
binary.compiler.sourcedeps += self.generated_headers
return binary
@property
def tag(self):
if builder.options.debug == "1":
return "Debug"
return "Release"
def detectProductVersion(self):
builder.AddConfigureFile("product.version")
# For OS X dylib versioning
import re
with open(os.path.join(builder.sourcePath, "product.version"), "r") as fp:
productContents = fp.read()
m = re.match("(\\d+)\.(\\d+)\.(\\d+).*", productContents)
if m == None:
self.productVersion = "1.0.0"
else:
major, minor, release = m.groups()
self.productVersion = "{0}.{1}.{2}".format(major, minor, release)
def detectSDKs(self):
sdk_list = builder.options.sdks.split(" ")
use_all = sdk_list[0] == "all"
use_present = sdk_list[0] == "present"
for sdk_name in PossibleSDKs:
sdk = PossibleSDKs[sdk_name]
if sdk.shouldBuild(self.all_targets):
if builder.options.hl2sdk_root:
sdk_path = os.path.join(
os.path.realpath(builder.options.hl2sdk_root), sdk.folder
)
else:
sdk_path = ResolveEnvPath(sdk.envvar, sdk.folder)
if sdk_path is None or not os.path.isdir(sdk_path):
if use_all or sdk_name in sdk_list:
raise Exception(
"Could not find a valid path for {0}".format(sdk.envvar)
)
continue
if use_all or use_present or sdk_name in sdk_list:
sdk.path = Normalize(sdk_path)
self.sdks[sdk_name] = sdk
if len(self.sdks) < 1:
raise Exception("At least one SDK must be available.")
if builder.options.sm_path:
self.sm_root = os.path.realpath(builder.options.sm_path)
else:
self.sm_root = ResolveEnvPath("SOURCEMOD", "sourcemod")
if not self.sm_root:
self.sm_root = ResolveEnvPath("SOURCEMOD_DEV", "sourcemod-central")
if not self.sm_root or not os.path.isdir(self.sm_root):
raise Exception("Could not find a source copy of SourceMod")
self.sm_root = Normalize(self.sm_root)
if builder.options.mms_path:
self.mms_root = builder.options.mms_path
else:
self.mms_root = ResolveEnvPath("MMSOURCE110", "mmsource-1.10")
if not self.mms_root:
self.mms_root = ResolveEnvPath("MMSOURCE", "metamod-source")
if not self.mms_root:
self.mms_root = ResolveEnvPath("MMSOURCE_DEV", "mmsource-central")
if not self.mms_root or not os.path.isdir(self.mms_root):
raise Exception("Could not find a source copy of Metamod:Source")
self.mms_root = Normalize(self.mms_root)
def configure(self):
if not set(self.target_archs).issubset(["x86", "x86_64"]):
raise Exception(
"Unknown target architecture: {0}".format(self.target_archs)
)
for cxx in self.all_targets:
self.configure_cxx(cxx)
def configure_cxx(self, cxx):
if cxx.family == "msvc":
if cxx.version < 1900:
raise Exception(
"Only MSVC 2015 and later are supported, c++14 support is required."
)
if cxx.family == "gcc":
if cxx.version < "gcc-4.9":
raise Exception(
"Only GCC versions 4.9 or greater are supported, c++14 support is required."
)
if cxx.family == "clang":
if cxx.version < "clang-3.4":
raise Exception(
"Only clang versions 3.4 or greater are supported, c++14 support is required."
)
if cxx.like("gcc"):
self.configure_gcc(cxx)
elif cxx.family == "msvc":
self.configure_msvc(cxx)
cxx.defines += ["HAVE_STRING_H"]
# Optimizaiton
if builder.options.opt == "1":
cxx.defines += ["NDEBUG"]
# Debugging
if builder.options.debug == "1":
cxx.defines += ["DEBUG", "_DEBUG"]
# Platform-specifics
if cxx.target.platform == "linux":
self.configure_linux(cxx)
elif cxx.target.platform == "mac":
self.configure_mac(cxx)
elif cxx.target.platform == "windows":
self.configure_windows(cxx)
cxx.includes += [
os.path.join(self.sm_root, "public"),
]
if self.use_auto_versioning():
cxx.defines += ["SM_GENERATED_BUILD"]
cxx.includes += [os.path.join(builder.buildPath, "includes")]
def configure_gcc(self, cxx):
cxx.defines += [
"stricmp=strcasecmp",
"_stricmp=strcasecmp",
"_snprintf=snprintf",
"_vsnprintf=vsnprintf",
"typeof=__typeof__",
"HAVE_STDINT_H",
"GNUC",
]
cxx.cflags += [
"-pipe",
"-fno-strict-aliasing",
"-Wall",
"-Wno-unused",
"-Wno-switch",
"-Wno-array-bounds",
"-msse",
"-fvisibility=hidden",
]
cxx.cxxflags += [
"-std=c++14",
"-fno-threadsafe-statics",
"-Wno-non-virtual-dtor",
"-Wno-overloaded-virtual",
"-fvisibility-inlines-hidden",
"-fpermissive",
]
have_gcc = cxx.vendor == "gcc"
have_clang = cxx.vendor == "clang"
if (
cxx.version >= "clang-3.9"
or cxx.version == "clang-3.4"
or cxx.version > "apple-clang-6.0"
):
cxx.cxxflags += ["-Wno-expansion-to-defined"]
if cxx.version == "clang-3.9" or cxx.version == "apple-clang-8.0":
cxx.cflags += ["-Wno-varargs"]
if cxx.version >= "clang-3.4" or cxx.version >= "apple-clang-7.0":
cxx.cxxflags += ["-Wno-inconsistent-missing-override"]
if cxx.version >= "clang-2.9" or cxx.version >= "apple-clang-3.0":
cxx.cxxflags += ["-Wno-null-dereference"]
if have_clang or (cxx.version >= "gcc-4.6"):
cxx.cflags += ["-Wno-narrowing"]
if have_clang or (cxx.version >= "gcc-4.7"):
cxx.cxxflags += ["-Wno-delete-non-virtual-dtor"]
if cxx.version >= "gcc-4.8":
cxx.cflags += ["-Wno-unused-result"]
if cxx.version >= "gcc-9.0":
cxx.cxxflags += ["-Wno-class-memaccess", "-Wno-packed-not-aligned"]
if have_clang:
cxx.cxxflags += ["-Wno-implicit-exception-spec-mismatch"]
if cxx.version >= "apple-clang-5.1" or cxx.version >= "clang-3.4":
cxx.cxxflags += ["-Wno-deprecated-register"]
else:
cxx.cxxflags += ["-Wno-deprecated"]
cxx.cflags += ["-Wno-sometimes-uninitialized"]
# Work around SDK warnings.
if cxx.version >= "clang-10.0":
cxx.cflags += [
"-Wno-implicit-int-float-conversion",
"-Wno-tautological-overlap-compare",
]
if have_gcc:
cxx.cflags += ["-mfpmath=sse"]
if builder.options.opt == "1":
cxx.cflags += ["-O3"]
def configure_msvc(self, cxx):
if builder.options.debug == "1":
cxx.cflags += ["/MTd"]
cxx.linkflags += ["/NODEFAULTLIB:libcmt"]
else:
cxx.cflags += ["/MT"]
cxx.defines += [
"_CRT_SECURE_NO_DEPRECATE",
"_CRT_SECURE_NO_WARNINGS",
"_CRT_NONSTDC_NO_DEPRECATE",
"_ITERATOR_DEBUG_LEVEL=0",
]
cxx.cflags += [
"/W3",
]
cxx.cxxflags += [
"/EHsc",
"/TP",
]
cxx.linkflags += [
"kernel32.lib",
"user32.lib",
"gdi32.lib",
"winspool.lib",
"comdlg32.lib",
"advapi32.lib",
"shell32.lib",
"ole32.lib",
"oleaut32.lib",
"uuid.lib",
"odbc32.lib",
"odbccp32.lib",
]
if builder.options.opt == "1":
cxx.cflags += ["/Ox", "/Zo"]
cxx.linkflags += ["/OPT:ICF", "/OPT:REF"]
if builder.options.debug == "1":
cxx.cflags += ["/Od", "/RTC1"]
# This needs to be after our optimization flags which could otherwise disable it.
# Don't omit the frame pointer.
cxx.cflags += ["/Oy-"]
def configure_linux(self, cxx):
cxx.defines += ["_LINUX", "POSIX"]
cxx.linkflags += ["-Wl,--exclude-libs,ALL", "-lm"]
if cxx.vendor == "gcc":
cxx.linkflags += ["-static-libgcc"]
elif cxx.vendor == "clang":
cxx.linkflags += ["-lgcc_eh"]
cxx.linkflags += ["-static-libstdc++"]
def configure_mac(self, cxx):
cxx.defines += ["OSX", "_OSX", "POSIX"]
cxx.cflags += ["-mmacosx-version-min=10.7"]
cxx.linkflags += [
"-mmacosx-version-min=10.7",
"-arch",
"i386",
"-lstdc++",
"-stdlib=libstdc++",
]
cxx.cxxflags += ["-stdlib=libstdc++"]
def configure_windows(self, cxx):
cxx.defines += ["WIN32", "_WINDOWS"]
def ConfigureForExtension(self, context, compiler):
compiler.cxxincludes += [
os.path.join(context.currentSourcePath),
os.path.join(context.currentSourcePath, "sdk"),
os.path.join(self.sm_root, "public"),
os.path.join(self.sm_root, "public", "extensions"),
os.path.join(self.sm_root, "sourcepawn", "include"),
os.path.join(self.sm_root, "public", "amtl", "amtl"),
os.path.join(self.sm_root, "public", "amtl"),
]
return compiler
def ConfigureForHL2(self, context, binary, sdk):
compiler = binary.compiler
SetArchFlags(compiler)
compiler.cxxincludes += [
os.path.join(self.mms_root, "core"),
os.path.join(self.mms_root, "core", "sourcehook"),
]
defines = ["RAD_TELEMETRY_DISABLED"]
defines += [
"SE_" + PossibleSDKs[i].define + "=" + PossibleSDKs[i].code
for i in PossibleSDKs
]
compiler.defines += defines
paths = [
["public"],
["public", "engine"],
["public", "mathlib"],
["public", "vstdlib"],
["public", "tier0"],
["public", "tier1"],
]
if sdk.name == "episode1" or sdk.name == "darkm":
paths.append(["public", "dlls"])
paths.append(["game_shared"])
else:
paths.append(["public", "game", "server"])
paths.append(["public", "toolframework"])
paths.append(["game", "shared"])
paths.append(["common"])
compiler.defines += ["SOURCE_ENGINE=" + sdk.code]
if sdk.name in ["sdk2013", "bms"] and compiler.like("gcc"):
# The 2013 SDK already has these in public/tier0/basetypes.h
compiler.defines.remove("stricmp=strcasecmp")
compiler.defines.remove("_stricmp=strcasecmp")
compiler.defines.remove("_snprintf=snprintf")
compiler.defines.remove("_vsnprintf=vsnprintf")
if compiler.like("msvc"):
compiler.defines += ["COMPILER_MSVC"]
if compiler.target.arch == "x86":
compiler.defines += ["COMPILER_MSVC32"]
elif compiler.target.arch == "x86_64":
compiler.defines += ["COMPILER_MSVC64"]
compiler.linkflags += ["legacy_stdio_definitions.lib"]
else:
compiler.defines += ["COMPILER_GCC"]
if compiler.target.arch == "x86_64":
compiler.defines += ["X64BITS", "PLATFORM_64BITS"]
# For everything after Swarm, this needs to be defined for entity networking
# to work properly with sendprop value changes.
if sdk.name in ["blade", "insurgency", "doi", "csgo"]:
compiler.defines += ["NETWORK_VARS_ENABLED"]
if sdk.name in [
"css",
"hl2dm",
"dods",
"sdk2013",
"bms",
"tf2",
"l4d",
"nucleardawn",
"l4d2",
]:
if compiler.target.platform in ["linux", "mac"]:
compiler.defines += ["NO_HOOK_MALLOC", "NO_MALLOC_OVERRIDE"]
if compiler.target.platform == "linux":
if sdk.name in ["csgo", "blade"]:
compiler.linkflags.remove("-static-libstdc++")
compiler.defines += ["_GLIBCXX_USE_CXX11_ABI=0"]
for path in paths:
compiler.cxxincludes += [os.path.join(sdk.path, *path)]
if compiler.target.platform == "linux":
if sdk.name == "episode1":
lib_folder = os.path.join(sdk.path, "linux_sdk")
elif (
sdk.name in ["tf2", "sdk2013", "bms"] and compiler.target.arch == "x86"
):
lib_folder = os.path.join(sdk.path, "lib", "public", "linux")
elif compiler.target.arch == "x86_64":
lib_folder = os.path.join(sdk.path, "lib", "public", "linux64")
else:
lib_folder = os.path.join(sdk.path, "lib", "linux")
elif compiler.target.platform == "mac":
if sdk.name in ["sdk2013", "bms"]:
lib_folder = os.path.join(sdk.path, "lib", "public", "osx32")
elif compiler.target.arch == "x86_64":
lib_folder = os.path.join(sdk.path, "lib", "osx64")
else:
lib_folder = os.path.join(sdk.path, "lib", "mac")
if compiler.target.platform in ["linux", "mac"]:
if sdk.name in ["sdk2013", "bms"] or compiler.target.arch == "x86_64":
compiler.postlink += [
os.path.join(lib_folder, "tier1.a"),
os.path.join(lib_folder, "mathlib.a"),
]
else:
compiler.postlink += [
os.path.join(lib_folder, "tier1_i486.a"),
os.path.join(lib_folder, "mathlib_i486.a"),
]
if sdk.name in ["blade", "insurgency", "doi", "csgo"]:
if compiler.target.arch == "x86_64":
compiler.postlink += [os.path.join(lib_folder, "interfaces.a")]
else:
compiler.postlink += [os.path.join(lib_folder, "interfaces_i486.a")]
dynamic_libs = []
if compiler.target.platform == "linux":
if sdk.name in [
"css",
"hl2dm",
"dods",
"tf2",
"sdk2013",
"bms",
"nucleardawn",
"l4d2",
"insurgency",
"doi",
]:
dynamic_libs = ["libtier0_srv.so", "libvstdlib_srv.so"]
elif compiler.target.arch == "x86_64" and sdk.name in ["csgo", "mock"]:
dynamic_libs = ["libtier0_client.so", "libvstdlib_client.so"]
elif sdk.name in ["l4d", "blade", "insurgency", "doi", "csgo"]:
dynamic_libs = ["libtier0.so", "libvstdlib.so"]
else:
dynamic_libs = ["tier0_i486.so", "vstdlib_i486.so"]
elif compiler.target.platform == "mac":
compiler.linkflags.append("-liconv")
dynamic_libs = ["libtier0.dylib", "libvstdlib.dylib"]
elif compiler.target.platform == "windows":
libs = ["tier0", "tier1", "vstdlib", "mathlib"]
if sdk.name in ["swarm", "blade", "insurgency", "doi", "csgo"]:
libs.append("interfaces")
for lib in libs:
if sdk.name in ["l4d", "l4d2"]:
# l4d and l4d2 engines don't have 64-bit libs
lib_path = os.path.join(sdk.path, "lib", "public", lib) + ".lib"
else:
if compiler.target.arch == "x86":
lib_path = (
os.path.join(sdk.path, "lib", "public", "x86", lib) + ".lib"
)
elif compiler.target.arch == "x86_64":
lib_path = (
os.path.join(sdk.path, "lib", "public", "x64", lib) + ".lib"
)
compiler.linkflags.append(lib_path)
for library in dynamic_libs:
source_path = os.path.join(lib_folder, library)
output_path = os.path.join(binary.localFolder, library)
# Ensure the output path exists.
context.AddFolder(binary.localFolder)
output = context.AddSymlink(source_path, output_path)
compiler.weaklinkdeps += [output]
compiler.linkflags[0:0] = [library]
return binary
def HL2Config(self, project, context, compiler, name, sdk):
binary = project.Configure(
compiler,
name,
"{0} - {1} {2}".format(self.tag, sdk.name, compiler.target.arch),
)
self.AddVersioning(binary)
return self.ConfigureForHL2(context, binary, sdk)
def HL2ExtConfig(self, project, context, compiler, name, sdk):
binary = project.Configure(
compiler,
name,
"{0} - {1} {2}".format(self.tag, sdk.name, compiler.target.arch),
)
self.AddVersioning(binary)
self.ConfigureForHL2(context, binary, sdk)
self.ConfigureForExtension(context, binary.compiler)
return binary
Extension = ExtensionConfig()
Extension.detectProductVersion()
Extension.detectSDKs()
Extension.configure()
if Extension.use_auto_versioning():
Extension.generated_headers = builder.Build("buildbot/Versioning")
builder.targets = builder.CloneableList(Extension.all_targets)
# Add additional buildscripts here
BuildScripts = ["src/AMBuilder", "buildbot/PackageScript"]
builder.Build(BuildScripts, {"Extension": Extension})