# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python : import os import subprocess import locale class AMXXConfig(object): def __init__(self): self.binaries = [] self.modules = [] self.plugins = {} self.libpc300 = None self.amxxpc = None self.metamod_path = None self.hlsdk_path = None self.mysql_path = None self.generated_headers = [] self.versionlib = None self.zlib = None self.hashing = None self.utf8rewind = None self.csx_app = None self.stdcxx_path = None self.nasm_path = None 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 detectMetamod(self): metamod_path = builder.options.metamod_path if not len(metamod_path): metamod_path = os.getenv('METAMOD', '') if len(metamod_path): self.metamod_path = os.path.join(builder.originalCwd, metamod_path) if not os.path.exists(os.path.join(self.metamod_path, 'metamod')): raise Exception('Metamod path does not exist: {0}'.format(metamod_path)) else: try_paths = [ os.path.join(builder.sourcePath, '..', 'metamod'), os.path.join(builder.sourcePath, '..', 'metamod-am'), os.path.join(builder.sourcePath, '..', 'metamod-hl1'), ] for try_path in try_paths: if os.path.exists(os.path.join(try_path, 'metamod')): self.metamod_path = os.path.normpath(try_path) break if not self.metamod_path: raise Exception('Could not find the source code to Metamod! Try passing --metamod to configure.py.') def detectHlsdk(self): hlsdk_path = builder.options.hlsdk_path if not len(hlsdk_path): hlsdk_path = os.getenv('HLSDK', '') if len(hlsdk_path): self.hlsdk_path = os.path.join(builder.originalCwd, hlsdk_path) if not os.path.exists(self.hlsdk_path): raise Exception('HLSDK path does not exist: {0}'.format(hlsdk_path)) else: try_paths = [ os.path.join(builder.sourcePath, '..', 'hlsdk'), ] for try_path in try_paths: if os.path.exists(try_path): self.hlsdk_path = os.path.normpath(try_path) break if not self.hlsdk_path: raise Exception('Could not find the HLSDK! Try passing --hlsdk to configure.py.') def detectMysql(self): if builder.options.disable_mysql: return mysql_path = builder.options.mysql_path if not len(mysql_path): mysql_path = os.getenv('MYSQL55', '') if len(mysql_path): self.mysql_path = os.path.join(builder.originalCwd, mysql_path) if not os.path.exists(self.mysql_path): raise Exception('MySQL path does not exist: {0}'.format(mysql_path)) else: try_paths = [ os.path.join(builder.sourcePath, '..', 'mysql-5.5'), ] for try_path in try_paths: if os.path.exists(try_path): self.mysql_path = os.path.normpath(try_path) break if not self.mysql_path: raise Exception('Could not find MySQL! Try passing --mysql to configure.py.') def detectNASM(self): import subprocess nasm_paths = [ getattr(builder.options, 'nasm_path', 'nasm'), ] if builder.target_platform == 'windows': nasm_paths += [os.path.join( builder.sourcePath, 'build_deps', 'nasm', 'nasm.exe') ] for nasm_path in nasm_paths: try: subprocess.check_output([nasm_path, '-v']) self.nasm_path = nasm_path break except: pass if self.nasm_path is None: raise Exception('Could not find a suitable path for nasm') # Returns list of lines of output from the compiler @staticmethod def invokeCompiler(args): if builder.compiler: p = subprocess.Popen(builder.compiler.argv + args, stdout=subprocess.PIPE) output = p.communicate()[0] if hasattr(output,'encoding') and output.encoding is not None: encoding = output.encoding else: encoding = locale.getpreferredencoding() return output.decode(encoding, 'replace').split('\n') return None def configure(self): builder.AddConfigureFile('pushbuild.txt') cxx = builder.DetectCompilers() if cxx.like('gcc'): self.configure_gcc(cxx) elif cxx.like('msvc'): self.configure_msvc(cxx) # Optimization if builder.options.opt == '1': cxx.defines += ['NDEBUG'] # Debugging if builder.options.debug == '1': cxx.defines += ['DEBUG', '_DEBUG'] # Platform-specifics if builder.target_platform == 'linux': self.configure_linux(cxx) elif builder.target_platform == 'mac': self.configure_mac(cxx) elif builder.target_platform == 'windows': self.configure_windows(cxx) # Finish up. cxx.defines += [ 'AMX_NOPROPLIST', 'PAWN_CELL_SIZE=32', 'AMXMODX_BUILD', 'AMXX_USE_VERSIONLIB', ] if self.use_auto_versioning(): cxx.defines += ['AMXX_GENERATED_BUILD'] cxx.includes += [os.path.join(builder.buildPath, 'includes')] cxx.includes += [os.path.join(builder.sourcePath, 'support', 'versionlib')] cxx.includes += [os.path.join(builder.sourcePath, 'public')] cxx.includes += [os.path.join(builder.sourcePath, 'public', 'sdk')] cxx.includes += [os.path.join(builder.sourcePath, 'public', 'amtl')] cxx.includes += [os.path.join(builder.sourcePath, 'public', 'amtl', 'amtl')] cxx.includes += [os.path.join(builder.sourcePath, 'public', 'memtools')] cxx.includes += [os.path.join(builder.sourcePath, 'third_party')] cxx.includes += [os.path.join(builder.sourcePath, 'third_party', 'hashing')] cxx.includes += [os.path.join(builder.sourcePath, 'third_party', 'zlib')] cxx.includes += [os.path.join(builder.sourcePath, 'third_party', 'utf8rewind')] def configure_gcc(self, cxx): cxx.cflags += [ '-pipe', '-fno-strict-aliasing', '-Wall', '-Werror', '-Wno-uninitialized', '-Wno-unused', '-Wno-switch', '-Wno-format', '-Wno-format-security', '-m32', ] cxx.cxxflags += [ '-Wno-invalid-offsetof', '-std=c++11', ] cxx.linkflags += ['-m32'] have_gcc = cxx.vendor == 'gcc' have_clang = cxx.vendor == 'clang' if have_clang or (have_gcc and cxx.version >= '4'): cxx.cflags += ['-fvisibility=hidden'] cxx.cxxflags += ['-fvisibility-inlines-hidden'] if have_clang or (have_gcc and cxx.version >= '4.6'): cxx.cflags += ['-Wno-narrowing'] if (have_gcc and cxx.version >= '4.7') or (have_clang and cxx.version >= '3'): cxx.cxxflags += ['-Wno-delete-non-virtual-dtor'] if have_gcc and cxx.version >= '4.8': cxx.cflags += ['-Wno-unused-result', '-Wno-error=sign-compare'] 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'] if builder.target_platform == 'linux' and cxx.version >= '3.6': cxx.cxxflags += ['-Wno-inconsistent-missing-override'] if builder.target_platform == 'linux' and cxx.version >= '3.9': cxx.cxxflags += ['-Wno-varargs'] if builder.target_platform == 'linux' and cxx.version >= '4.0': cxx.cxxflags += ['-Wno-address-of-packed-member'] if have_gcc: cxx.cflags += ['-Wno-parentheses'] cxx.c_only_flags += ['-std=c99'] elif have_clang: cxx.cflags += ['-Wno-logical-op-parentheses'] cxx.cxxflags += [ '-fno-exceptions', '-fno-rtti', ] if builder.options.opt == '1': cxx.cflags += ['-O2'] 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', '/GR-', '/TP', ] cxx.linkflags += [ '/MACHINE:X86', '/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', ] if cxx.version >= 1900: cxx.linkflags += ['legacy_stdio_definitions.lib', 'legacy_stdio_wide_specifiers.lib'] if builder.options.opt == '1': cxx.cflags += ['/Ox'] 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', 'LINUX'] cxx.linkflags += ['-ldl', '-lm'] if cxx.vendor == 'gcc': cxx.linkflags += ['-static-libgcc'] elif cxx.vendor == 'clang': cxx.linkflags += ['-lgcc_eh'] if cxx.like('gcc'): self.stdcxx_path = self.invokeCompiler(['-m32', '-print-file-name=' + 'libstdc++.a'])[0] def configure_mac(self, cxx): cxx.defines += ['OSX', '_OSX', 'POSIX'] cxx.cflags += ['-mmacosx-version-min=10.5'] cxx.linkflags += [ '-mmacosx-version-min=10.5', '-arch', 'i386', '-lstdc++', '-stdlib=libc++', '-framework', 'CoreServices', ] cxx.cxxflags += ['-stdlib=libc++'] def configure_windows(self, cxx): cxx.defines += ['WIN32', '_WINDOWS'] # # Low-level compiler and binary construction. # def ConfigureForModule(self, context, compiler): compiler.cxxincludes += [ os.path.join(context.currentSourcePath), os.path.join(context.currentSourcePath, 'sdk'), os.path.join(self.metamod_path, 'metamod'), os.path.join(self.hlsdk_path, 'common'), os.path.join(self.hlsdk_path, 'dlls'), os.path.join(self.hlsdk_path, 'engine'), os.path.join(self.hlsdk_path, 'game_shared'), os.path.join(self.hlsdk_path, 'public'), os.path.join(self.hlsdk_path, 'pm_shared'), ] return compiler def AddVersioning(self, binary): if builder.target_platform == 'windows': binary.compiler.rcdefines += [ 'BINARY_NAME="{0}"'.format(binary.outputFile), 'RC_COMPILE', ] if self.use_auto_versioning(): binary.compiler.rcdefines += ['AMXX_GENERATED_BUILD'] elif builder.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.linkflags += [self.versionlib] binary.compiler.sourcedeps += AMXX.generated_headers return binary # # High level job construction for libraries, metamod plugins, modules, and # executables. # def Library(self, context, name): binary = context.compiler.Library(name) return self.AddVersioning(binary) def MetaPlugin(self, context, name): if builder.target_platform == 'mac' or builder.target_platform == 'windows': name = name + '_mm' elif builder.target_platform == 'linux': name = name + '_mm_i386' binary = context.compiler.Library(name) self.ConfigureForModule(context, binary.compiler) return self.AddVersioning(binary) def MetaModule(self, context, name): if builder.target_platform == 'mac' or builder.target_platform == 'windows': name = name + '_amxx' elif builder.target_platform == 'linux': name = name + '_amxx_i386' binary = context.compiler.Library(name) self.ConfigureForModule(context, binary.compiler) return self.AddVersioning(binary) def Program(self, context, name): binary = context.compiler.Program(name) return self.AddVersioning(binary) def AddAssembly(self, context, binary, input_file, output_file, includes=[], extra_argv=[]): if builder.target_platform == 'windows': obj_type = 'win32' elif builder.target_platform == 'linux': obj_type = 'elf32' elif builder.target_platform == 'mac': obj_type = 'macho32' input_path = os.path.join(context.currentSourcePath, input_file) output_path = output_file argv = [ self.nasm_path, '-I{0}{1}'.format(context.currentSourcePath, os.sep), input_path, '-f', obj_type, '-o', output_path, ] + extra_argv extra_includes = [] for include_file in includes: extra_includes.append(os.path.join(context.currentSourcePath, include_file)) cmd_node, output_nodes = context.AddCommand( inputs = [input_path] + extra_includes, argv = argv, outputs = [output_path]) binary.compiler.linkflags += [output_nodes[0]] AMXX = AMXXConfig() AMXX.detectProductVersion() AMXX.detectMetamod() AMXX.detectHlsdk() AMXX.detectMysql() AMXX.detectNASM() AMXX.configure() if AMXX.use_auto_versioning(): AMXX.generated_headers = builder.RunScript( 'support/Versioning', { 'AMXX': AMXX } ) AMXX.versionlib = builder.RunScript( 'support/versionlib/AMBuilder', { 'AMXX': AMXX } ) AMXX.zlib = builder.RunScript( 'third_party/zlib/AMBuilder' ) AMXX.hashing = builder.RunScript( 'third_party/hashing/AMBuilder' ) AMXX.utf8rewind = builder.RunScript( 'third_party/utf8rewind/AMBuilder' ) builder.RunBuildScripts( [ 'amxmodx/AMBuilder', 'compiler/amxxpc/AMBuilder', 'compiler/libpc300/AMBuilder', 'modules/cstrike/cstrike/AMBuilder', 'modules/cstrike/csx/AMBuilder', 'modules/dod/dodfun/AMBuilder', 'modules/dod/dodx/AMBuilder', 'modules/engine/AMBuilder', 'modules/fakemeta/AMBuilder', 'modules/fun/AMBuilder', 'modules/geoip/AMBuilder', 'modules/hamsandwich/AMBuilder', 'modules/json/AMBuilder', 'modules/mysqlx/AMBuilder', 'modules/ns/AMBuilder', 'modules/nvault/AMBuilder', 'modules/regex/AMBuilder', 'modules/sockets/AMBuilder', 'modules/sqlite/AMBuilder', 'modules/tfcx/AMBuilder', 'modules/ts/tsfun/AMBuilder', 'modules/ts/tsx/AMBuilder', ], { 'AMXX': AMXX } ) # The csstats.dat reader is Windows-only. if builder.target_platform == 'windows': builder.RunScript('modules/cstrike/csx/WinCSX/AMBuilder', { 'AMXX': AMXX }) if builder.backend == 'amb2': builder.RunBuildScripts([ 'plugins/AMBuilder', 'support/PackageScript', ], { 'AMXX': AMXX } )