# 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})