# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python: import json import os def get_this_file_path(): return get_this_file_path.__code__.co_filename class SdkLibBuilder(object): def __init__(self, sdk, name, cxx): self.targets = [cxx.clone()] self.libs = [] self.sdk = sdk self.name = name def ConfigureLibrary(self, project, compiler, context, prefix = ''): name = prefix + project.name binary = project.Configure(compiler, '{0}_{1}'.format(self.sdk['name'], name), '{0} - {1} - {2}'.format(self.sdk['name'], self.name, compiler.target.arch)) binary.compiler.cxxincludes += [ os.path.join(context.currentSourcePath) ] SdkHelpers.configureCxx(context, binary, self.sdk) return binary class SdkTarget(object): def __init__(self, context, sdk, cxx): self.sdk = sdk self.cxx = cxx # Find protoc self.protoc = None rel_protoc_path = sdk[cxx.target.platform].get('protoc_path', None) if rel_protoc_path: protoc_path = os.path.join(sdk['path'], rel_protoc_path) self.protoc = context.DetectProtoc(path = protoc_path) for path in sdk['include_paths']: self.protoc.includes += [os.path.join(sdk['path'], path)] # Find tier1 tier1 = SdkTarget.findLibrary(context, sdk, cxx, 'tier1') # Find mathlib mathlib = SdkTarget.findLibrary(context, sdk, cxx, 'mathlib') # Only append the libs at the end, so they aren't found during the configureCxx step if not 'tier1' in sdk : sdk['tier1'] = {} if not 'mathlib' in sdk : sdk['mathlib'] = {} sdk['tier1'][cxx.target.arch] = tier1 sdk['mathlib'][cxx.target.arch] = mathlib @staticmethod def findLibrary(context, sdk, cxx, name): # Mock libs are currently unhandled (different ambuild scripts) if sdk['name'] == 'mock': return None ambuilder = os.path.join(sdk['path'], name, 'AMBuilder') if os.path.exists(ambuilder): ambuilder = os.path.abspath(ambuilder) libbuilders = SdkLibBuilder(sdk, name, cxx) oldSource = context.cm.sourcePath context.cm.sourcePath = os.path.dirname(ambuilder) libcontext = context.Build('AMBuilder', { 'HL2SDK': libbuilders }) context.cm.sourcePath = oldSource if len(libbuilders.libs) != 1: raise Exception('No lib found for {0}'.format(name)) return libbuilders.libs[0] return None class SdkHelpers(object): def __init__(self): self.sdks = {} self.sdk_manifests = [] self.sdk_filter = None self.find_sdk_path = None self.sdk_targets = [] # find_sdk_path must be set to use. def findSdks(self, builder, cxx_list, sdk_list): not_found = [] sdk_remaining = set(sdk_list) for sdk_name, sdk in SdkHelpers.getSdks(builder): self.sdk_manifests.append(sdk) # Skip SDKs that weren't specified or are not supported. if not self.shouldFindSdk(sdk, sdk_list): continue # Skip SDKs that won't build on any targets. if not SdkHelpers.sdkHasBuildTargets(sdk, cxx_list): continue sdk_path = self.find_sdk_path(sdk_name) if sdk_path is None: if SdkHelpers.shouldRequireSdk(sdk_name, sdk_list): raise Exception('Could not find a valid path for {0}'.format(sdk_name)) not_found.append(sdk_name) continue sdk_remaining.discard(sdk_name) sdk['path'] = sdk_path self.sdks[sdk_name] = sdk for cxx in cxx_list: if SdkHelpers.shouldBuildSdk(sdk, cxx): self.sdk_targets += [SdkTarget(builder, sdk, cxx)] if 'present' in sdk_list: for sdk in not_found: print('Warning: hl2sdk-{} was not found, and will not be included in build.'.format(sdk)) elif len(sdk_remaining) and 'all' not in sdk_list: for sdk in sdk_remaining: print('Error: hl2sdk-{} was not found.'.format(sdk)) raise Exception('Missing hl2sdks: {}'.format(','.join(sdk_remaining))) if not len(self.sdk_targets) and len(sdk_list): raise Exception('No buildable SDKs were found, nothing to build.') @staticmethod def shouldRequireSdk(sdk_name, sdk_list): if 'all' in sdk_list: return sdk_name != 'mock' if sdk_name in sdk_list: return True return 'present' not in sdk_list def shouldFindSdk(self, sdk, sdk_list): # Remove SDKs that the project doesn't support. if self.sdk_filter and not self.sdk_filter(sdk): return False if 'all' in sdk_list or 'present' in sdk_list: return True return sdk['name'] in sdk_list @staticmethod def sdkHasBuildTargets(sdk, cxx_list): for cxx in cxx_list: if SdkHelpers.shouldBuildSdk(sdk, cxx): return True return False @staticmethod def shouldBuildSdk(sdk, cxx): if cxx.target.platform in sdk['platforms']: if cxx.target.arch in sdk['platforms'][cxx.target.platform]: return True return False @staticmethod def addLists(sdk, list_name, cxx): result = SdkHelpers.getLists(sdk, list_name, cxx) cxx_list = getattr(cxx, list_name) cxx_list.extend(result) @staticmethod def getLists(sdk, list_name, cxx): result = SdkHelpers.getListsImpl(sdk, list_name, cxx) if Project in sdk: result += SdkHelpers.getListsImpl(sdk[Project], list_name, cxx) return result @staticmethod def getListsImpl(info, list_name, cxx): result = [] if cxx.target.platform in info: platform_info = info[cxx.target.platform] result += platform_info.get(list_name, []) if cxx.target.arch in platform_info: arch_info = platform_info[cxx.target.arch] result += arch_info.get(list_name, []) return result @staticmethod def getSdks(builder): try: # Requires new enough ambuild version for __file__ to be defined sdk_manifest_dir = os.path.join(os.path.dirname(__file__), 'manifests') except NameError: sdk_manifest_dir = os.path.join(os.path.dirname(get_this_file_path()), 'manifests') out = [] for sdk_manifest in os.listdir(sdk_manifest_dir): sdk_name, _ = os.path.splitext(sdk_manifest) sdk_manifest_path = os.path.join(sdk_manifest_dir, sdk_manifest) with open(sdk_manifest_path, 'rt') as fp: sdk = json.load(fp) builder.AddConfigureFile(sdk_manifest_path) out.append((sdk_name, sdk)) return out @staticmethod def addLibrary(context, binary, sdk, name): ambuilder = os.path.join(sdk['path'], name, 'AMBuilder') if os.path.exists(ambuilder): builder = SdkLibBuilder(sdk['name'], binary.compiler) context.Build([ambuilder], { 'HL2SDK': builder }) if len(builder.libs) != 1: raise Exception('No lib found for {0}'.format(name)) task = builder.libs[0] binary.compiler.postlink += [os.path.join(context.buildPath, task.binary.path)] binary.compiler.linkdeps += [task.binary] @staticmethod def configureCxx(context, binary, sdk): cxx = binary.compiler # Includes/defines. cxx.defines += ['SOURCE_ENGINE={}'.format(sdk['code'])] cxx.defines += ['GAME_DLL', 'RAD_TELEMETRY_DISABLED'] if sdk['name'] in ['sdk2013', 'bms', 'pvkii', 'tf2', 'css', 'dods', 'hl2dm', 'l4d2'] and cxx.like('gcc'): # The 2013 SDK already has these in public/tier0/basetypes.h rm_defines = [ 'stricmp=strcasecmp', '_stricmp=strcasecmp', '_snprintf=snprintf', '_vsnprintf=vsnprintf,' ] for rm_define in rm_defines: if rm_define in cxx.defines: try: cxx.defines.remove(rm_define) except ValueError: pass if cxx.family == 'msvc': cxx.defines += ['COMPILER_MSVC'] if cxx.target.arch == 'x86': cxx.defines += ['COMPILER_MSVC32', 'WIN32'] elif cxx.target.arch == 'x86_64': cxx.defines += ['COMPILER_MSVC64', 'WIN32', 'WIN64'] if cxx.version >= 1900: cxx.linkflags += ['legacy_stdio_definitions.lib'] else: cxx.defines += ['COMPILER_GCC', '_LINUX', 'LINUX', 'POSIX', 'GNUC'] if cxx.target.arch == 'x86_64': cxx.defines += ['X64BITS', 'PLATFORM_64BITS'] SdkHelpers.addLists(sdk, 'defines', cxx) SdkHelpers.addLists(sdk, 'linkflags', cxx) for path in sdk['include_paths']: cxx.cxxincludes += [os.path.join(sdk['path'], path)] # Link steps. for lib in SdkHelpers.getLists(sdk, 'libs', cxx): cxx.linkflags += [os.path.join(sdk['path'], lib)] for lib in SdkHelpers.getLists(sdk, 'postlink_libs', cxx): cxx.postlink += [os.path.join(sdk['path'], lib)] dynamic_libs = SdkHelpers.getLists(sdk, 'dynamic_libs', cxx) for library in dynamic_libs: file_name = os.path.split(library)[1] source_path = os.path.join(sdk['path'], library) output_path = os.path.join(binary.localFolder, file_name) context.AddFolder(binary.localFolder) output = context.AddSymlink(source_path, output_path) cxx.weaklinkdeps += [output] cxx.linkflags[0:0] = [file_name] # cflags for cflag in SdkHelpers.getLists(sdk, 'cflags', cxx): cxx.cflags += [cflag] # cxxflags for cxxflag in SdkHelpers.getLists(sdk, 'cxxflags', cxx): cxx.cxxflags += [cxxflag] if cxx.target.platform == 'linux': cxx.linkflags[0:0] = ['-lm'] if cxx.target.platform == 'linux': if sdk[cxx.target.platform]['uses_system_cxxlib']: try: cxx.linkflags.remove('-static-libstdc++') except ValueError: pass cxx.linkflags += ['-lstdc++'] if 'tier1' in sdk and cxx.target.arch in sdk['tier1']: task = sdk['tier1'][cxx.target.arch] if task != None: cxx.postlink += [os.path.join(context.buildPath, task.binary.path)] cxx.linkdeps += [task.binary] if 'mathlib' in sdk and cxx.target.arch in sdk['mathlib']: task = sdk['mathlib'][cxx.target.arch] if task != None: cxx.postlink += [os.path.join(context.buildPath, task.binary.path)] cxx.linkdeps += [task.binary] rvalue = SdkHelpers()