mirror of
https://github.com/alliedmodders/metamod-source.git
synced 2025-03-28 14:50:19 +01:00
Rather than hardcode a bunch of SDK stuff, this is an attempt to move SDK information to a declarative model. Each SDK gets a manifest, and the manifests are stored in a shared repository. Manifests encode stuff like "what platforms does this SDK build on" and "what link flags do I need on each architecture". This will hopefully reduce the complexity of the build scripts, since going forward we only have to add new manifests, rather than figure out how to attach more gunk into the build logic.
450 lines
14 KiB
Python
450 lines
14 KiB
Python
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
|
|
import os, sys
|
|
|
|
def ResolveEnvPath(env, folder):
|
|
if env in os.environ:
|
|
path = os.environ[env]
|
|
if os.path.isdir(path):
|
|
return path
|
|
else:
|
|
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
|
|
|
|
SdkHelpers = builder.Eval('hl2sdk-manifests/SdkHelpers.ambuild', {
|
|
'Project': 'metamod'
|
|
})
|
|
|
|
class MMSConfig(object):
|
|
def __init__(self):
|
|
self.sdk_manifests = []
|
|
self.sdks = {}
|
|
self.sdk_targets = []
|
|
self.binaries = []
|
|
self.generated_headers = None
|
|
self.versionlib = None
|
|
self.all_targets = []
|
|
self.target_archs = set()
|
|
|
|
if builder.options.targets:
|
|
target_archs = builder.options.targets.split(',')
|
|
else:
|
|
target_archs = ['x86']
|
|
if builder.backend == 'amb2':
|
|
target_archs.append('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):
|
|
if builder.backend != 'amb2':
|
|
return False
|
|
return not getattr(builder.options, 'disable_auto_versioning', False)
|
|
|
|
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 findSdkPath(self, sdk_name):
|
|
dir_name = 'hl2sdk-{}'.format(sdk_name)
|
|
if builder.options.hl2sdk_root:
|
|
sdk_path = os.path.join(builder.options.hl2sdk_root, dir_name)
|
|
if os.path.exists(sdk_path):
|
|
return sdk_path
|
|
return ResolveEnvPath('HL2SDK{}'.format(sdk_name.upper()), dir_name)
|
|
|
|
def shouldIncludeSdk(self, sdk):
|
|
if sdk.get('source2', False) and self.productVersion.startswith('1.'):
|
|
return False
|
|
for cxx in self.all_targets:
|
|
if SdkHelpers.shouldBuildSdk(sdk, cxx):
|
|
return True
|
|
return False
|
|
|
|
def detectSDKs(self):
|
|
sdk_list = builder.options.sdks.split(',')
|
|
use_all = sdk_list[0] == 'all'
|
|
use_present = sdk_list[0] == 'present'
|
|
if sdk_list[0] == '':
|
|
sdk_list = []
|
|
|
|
not_found = []
|
|
for sdk_name, sdk in SdkHelpers.getSdks(builder):
|
|
self.sdk_manifests.append(sdk)
|
|
if not self.shouldIncludeSdk(sdk):
|
|
continue
|
|
|
|
sdk_path = self.findSdkPath(sdk_name)
|
|
if sdk_path is None:
|
|
if (use_all and sdk_name != 'mock') or sdk_name in sdk_list:
|
|
raise Exception('Could not find a valid path for {0}'.format(sdk_name))
|
|
not_found.append(sdk_name)
|
|
continue
|
|
|
|
sdk['path'] = sdk_path
|
|
self.sdks[sdk_name] = sdk
|
|
|
|
if len(self.sdks) < 1 and len(sdk_list):
|
|
raise Exception('No SDKs were found, nothing to build.')
|
|
|
|
if use_present:
|
|
for sdk in not_found:
|
|
print('Warning: hl2sdk-{} was not found, and will not be included in build.'.format(sdk))
|
|
|
|
for _, sdk in self.sdks.items():
|
|
for cxx in self.all_targets:
|
|
if not SdkHelpers.shouldBuildSdk(sdk, cxx):
|
|
continue
|
|
self.sdk_targets += [(sdk, cxx)]
|
|
|
|
def configure(self):
|
|
builder.AddConfigureFile('pushbuild.txt')
|
|
|
|
for cxx in self.all_targets:
|
|
if cxx.target.arch not in ['x86', 'x86_64']:
|
|
raise Exception('Unknown target architecture: {0}'.format(arch))
|
|
|
|
self.configure_cxx(cxx)
|
|
|
|
def configure_cxx(self, cxx):
|
|
if cxx.behavior == 'gcc':
|
|
cxx.defines += [
|
|
'stricmp=strcasecmp',
|
|
'_stricmp=strcasecmp',
|
|
'_snprintf=snprintf',
|
|
'_vsnprintf=vsnprintf',
|
|
'HAVE_STDINT_H',
|
|
'GNUC',
|
|
]
|
|
cxx.cflags += [
|
|
'-pipe',
|
|
'-fno-strict-aliasing',
|
|
'-Wall',
|
|
'-Werror',
|
|
'-Wno-uninitialized',
|
|
'-Wno-unused',
|
|
'-Wno-switch',
|
|
'-msse',
|
|
'-fPIC',
|
|
]
|
|
|
|
if cxx.version == 'apple-clang-6.0' or cxx.version == 'clang-3.4':
|
|
cxx.cxxflags += ['-std=c++1y']
|
|
else:
|
|
cxx.cxxflags += ['-std=c++14']
|
|
if (cxx.version >= 'gcc-4.0') or cxx.family == 'clang':
|
|
cxx.cflags += ['-fvisibility=hidden']
|
|
cxx.cxxflags += ['-fvisibility-inlines-hidden']
|
|
cxx.cxxflags += [
|
|
'-fno-exceptions',
|
|
'-fno-rtti',
|
|
'-fno-threadsafe-statics',
|
|
'-Wno-non-virtual-dtor',
|
|
'-Wno-overloaded-virtual',
|
|
]
|
|
if (cxx.version >= 'gcc-4.7' or cxx.family == 'clang'):
|
|
cxx.cxxflags += ['-Wno-delete-non-virtual-dtor']
|
|
if cxx.family == 'gcc':
|
|
cxx.cflags += ['-mfpmath=sse']
|
|
if cxx.family == 'clang':
|
|
cxx.cxxflags += ['-Wno-implicit-exception-spec-mismatch']
|
|
if cxx.version >= 'clang-3.9' or cxx.version >= 'apple-clang-10.0':
|
|
cxx.cxxflags += ['-Wno-expansion-to-defined']
|
|
if cxx.version >= 'clang-3.6' or cxx.version >= 'apple-clang-7.0':
|
|
cxx.cxxflags += ['-Wno-inconsistent-missing-override']
|
|
if cxx.version >= 'apple-clang-5.1' or cxx.version >= 'clang-3.4':
|
|
cxx.cxxflags += ['-Wno-deprecated-register']
|
|
else:
|
|
cxx.cxxflags += ['-Wno-deprecated']
|
|
|
|
# Work around SDK warnings.
|
|
if cxx.version >= 'clang-10.0' or cxx.version >= 'apple-clang-12.0':
|
|
cxx.cflags += [
|
|
'-Wno-implicit-int-float-conversion',
|
|
'-Wno-tautological-overlap-compare',
|
|
]
|
|
|
|
elif cxx.like('msvc'):
|
|
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',
|
|
]
|
|
cxx.cflags += [
|
|
'/W3',
|
|
'/Zi',
|
|
]
|
|
cxx.cxxflags += ['/TP']
|
|
|
|
cxx.linkflags += [
|
|
'/SUBSYSTEM:WINDOWS',
|
|
'kernel32.lib',
|
|
'user32.lib',
|
|
'gdi32.lib',
|
|
'winspool.lib',
|
|
'comdlg32.lib',
|
|
'advapi32.lib',
|
|
'shell32.lib',
|
|
'ole32.lib',
|
|
'oleaut32.lib',
|
|
'uuid.lib',
|
|
'odbc32.lib',
|
|
'odbccp32.lib',
|
|
]
|
|
|
|
# Optimization
|
|
if builder.options.opt == '1':
|
|
cxx.defines += ['NDEBUG']
|
|
if cxx.behavior == 'gcc':
|
|
cxx.cflags += ['-O3']
|
|
elif cxx.behavior == 'msvc':
|
|
cxx.cflags += ['/Ox', '/Zo']
|
|
cxx.linkflags += ['/OPT:ICF', '/OPT:REF']
|
|
|
|
# Debugging
|
|
if builder.options.debug == '1':
|
|
cxx.defines += ['DEBUG', '_DEBUG']
|
|
if cxx.behavior == 'gcc':
|
|
cxx.cflags += ['-g3']
|
|
elif cxx.behavior == 'msvc':
|
|
cxx.cflags += ['/Od', '/RTC1']
|
|
|
|
# Don't omit the frame pointer.
|
|
# This needs to be after our optimization flags which could otherwise disable it.
|
|
if cxx.behavior == 'gcc':
|
|
cxx.cflags += ['-fno-omit-frame-pointer']
|
|
elif cxx.behavior == 'msvc':
|
|
cxx.cflags += ['/Oy-']
|
|
|
|
# Platform-specifics
|
|
if cxx.target.platform == 'linux':
|
|
cxx.defines += ['_LINUX', 'POSIX', '_FILE_OFFSET_BITS=64']
|
|
if cxx.family == 'gcc':
|
|
cxx.linkflags += ['-static-libgcc']
|
|
elif cxx.family == 'clang':
|
|
cxx.linkflags += ['-lgcc_eh']
|
|
elif cxx.target.platform == 'mac':
|
|
cxx.defines += ['OSX', '_OSX', 'POSIX']
|
|
|
|
if cxx.version >= 'apple-clang-10.0':
|
|
cxx.cflags += ['-mmacosx-version-min=10.9', '-stdlib=libc++']
|
|
cxx.linkflags += [
|
|
'-mmacosx-version-min=10.9',
|
|
]
|
|
else:
|
|
cxx.cflags += ['-mmacosx-version-min=10.5']
|
|
cxx.linkflags += [
|
|
'-mmacosx-version-min=10.5',
|
|
]
|
|
|
|
cxx.linkflags += [
|
|
'-lc++',
|
|
]
|
|
elif cxx.target.platform == 'windows':
|
|
cxx.defines += ['WIN32', '_WINDOWS']
|
|
|
|
# Finish up.
|
|
cxx.defines += [ 'MMS_USE_VERSIONLIB' ]
|
|
cxx.includes += [
|
|
os.path.join(builder.sourcePath, 'public'),
|
|
os.path.join(builder.sourcePath, 'third_party', 'amtl'),
|
|
]
|
|
if self.use_auto_versioning():
|
|
cxx.defines += ['MMS_GENERATED_BUILD']
|
|
cxx.includes += [
|
|
os.path.join(builder.buildPath, 'includes'),
|
|
os.path.join(builder.sourcePath, 'versionlib'),
|
|
]
|
|
|
|
def HL2Compiler(self, context, cxx, sdk):
|
|
compiler = cxx.clone()
|
|
compiler.cxxincludes += [
|
|
os.path.join(context.currentSourcePath),
|
|
os.path.join(context.currentSourcePath, 'sourcehook'),
|
|
os.path.join(context.sourcePath, 'loader'),
|
|
]
|
|
|
|
defines = []
|
|
for other_sdk in self.sdk_manifests:
|
|
defines += ['SE_{}={}'.format(other_sdk['define'], other_sdk['code'])]
|
|
|
|
compiler.defines += defines
|
|
compiler.defines += ['SOURCE_ENGINE=' + sdk['code']]
|
|
|
|
if sdk['name'] in ['sdk2013', 'bms', 'pvkii'] 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.family == 'msvc':
|
|
compiler.defines += ['COMPILER_MSVC']
|
|
if compiler.target.arch == 'x86':
|
|
compiler.defines += ['COMPILER_MSVC32']
|
|
elif compiler.target.arch == 'x86_64':
|
|
compiler.defines += ['COMPILER_MSVC64']
|
|
|
|
if compiler.version >= 1900:
|
|
compiler.linkflags += ['legacy_stdio_definitions.lib']
|
|
else:
|
|
compiler.defines += ['COMPILER_GCC']
|
|
|
|
if compiler.target.arch == 'x86_64':
|
|
compiler.defines += ['X64BITS', 'PLATFORM_64BITS']
|
|
|
|
SdkHelpers.addLists(sdk, 'defines', compiler)
|
|
SdkHelpers.addLists(sdk, 'linkflags', compiler)
|
|
|
|
if sdk['name'] in ['dota', 'cs2']:
|
|
compiler.defines += ['META_IS_SOURCE2']
|
|
|
|
for path in sdk['include_paths']:
|
|
compiler.cxxincludes += [os.path.join(sdk['path'], path)]
|
|
|
|
return compiler
|
|
|
|
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' and binary.type == 'library':
|
|
binary.compiler.postlink += [
|
|
'-compatibility_version', '1.0.0',
|
|
'-current_version', self.productVersion
|
|
]
|
|
if self.use_auto_versioning():
|
|
binary.compiler.linkflags += [self.versionlib[binary.compiler.target.arch]]
|
|
binary.compiler.sourcedeps += MMS.generated_headers
|
|
if builder.options.breakpad_dump:
|
|
binary.compiler.symbol_files = 'separate'
|
|
return binary
|
|
|
|
def Library(self, cxx, name):
|
|
binary = cxx.Library(name)
|
|
self.AddVersioning(binary)
|
|
return binary
|
|
|
|
def Program(self, compiler, name):
|
|
binary = compiler.Program(name)
|
|
compiler = binary.compiler
|
|
|
|
self.AddVersioning(binary)
|
|
if '-static-libgcc' in binary.compiler.linkflags:
|
|
binary.compiler.linkflags.remove('-static-libgcc')
|
|
if '-lgcc_eh' in binary.compiler.linkflags:
|
|
binary.compiler.linkflags.remove('-lgcc_eh')
|
|
if binary.compiler.like('gcc'):
|
|
binary.compiler.linkflags += ['-lstdc++']
|
|
return binary
|
|
|
|
def StaticLibrary(self, cxx, name):
|
|
return cxx.StaticLibrary(name)
|
|
|
|
def HL2Library(self, context, compiler, name, sdk):
|
|
compiler = self.HL2Compiler(context, compiler, sdk)
|
|
|
|
lib_folder = sdk[compiler.target.platform][compiler.target.arch]['lib_folder']
|
|
lib_folder = os.path.join(sdk['path'], lib_folder)
|
|
for lib in SdkHelpers.getLists(sdk, 'libs', compiler):
|
|
compiler.linkflags += [os.path.join(sdk['path'], lib_folder, lib)]
|
|
for lib in SdkHelpers.getLists(sdk, 'postlink_libs', compiler):
|
|
compiler.postlink += [os.path.join(sdk['path'], lib_folder, lib)]
|
|
|
|
binary = self.Library(compiler, name)
|
|
compiler = binary.compiler
|
|
|
|
if compiler.target.platform == 'linux':
|
|
compiler.linkflags[0:0] = ['-lm']
|
|
elif compiler.target.platform == 'mac':
|
|
binary.compiler.linkflags.append('-liconv')
|
|
|
|
dynamic_libs = SdkHelpers.getLists(sdk, 'dynamic_libs', compiler)
|
|
for library in dynamic_libs:
|
|
source_path = os.path.join(lib_folder, library)
|
|
output_path = os.path.join(binary.localFolder, library)
|
|
|
|
context.AddFolder(binary.localFolder)
|
|
output = context.AddSymlink(source_path, output_path)
|
|
|
|
binary.compiler.weaklinkdeps += [output]
|
|
binary.compiler.linkflags[0:0] = [library]
|
|
|
|
return binary
|
|
|
|
if getattr(builder, 'target', None) is not None:
|
|
sys.stderr.write("Your output folder was configured for AMBuild 2.1, and Metamod:Source\n")
|
|
sys.stderr.write("is now configured to use AMBuild 2.2. Please remove your output folder\n")
|
|
sys.stderr.write("and reconfigure to continue.\n")
|
|
os._exit(1)
|
|
|
|
MMS = MMSConfig()
|
|
MMS.detectProductVersion()
|
|
MMS.detectSDKs()
|
|
MMS.configure()
|
|
|
|
if MMS.use_auto_versioning():
|
|
MMS.generated_headers = builder.Build(
|
|
'support/buildbot/Versioning',
|
|
{ 'MMS': MMS }
|
|
)
|
|
MMS.versionlib = builder.Build(
|
|
'versionlib/AMBuildScript',
|
|
{ 'MMS': MMS }
|
|
)
|
|
|
|
BuildScripts = [
|
|
'loader/AMBuilder',
|
|
'core/AMBuilder',
|
|
]
|
|
if getattr(builder.options, 'enable_tests', False):
|
|
BuildScripts += [
|
|
'core/sourcehook/test/AMBuilder',
|
|
]
|
|
|
|
if builder.backend == 'amb2':
|
|
BuildScripts += [
|
|
'support/buildbot/PackageScript',
|
|
]
|
|
|
|
builder.Build(BuildScripts, { 'MMS': MMS })
|
|
|
|
if builder.options.breakpad_dump:
|
|
builder.Build('support/buildbot/BreakpadSymbols', { 'MMS': MMS })
|