1 # vim: set ts=2 sw=2 tw=99 noet ft=python:
4 from ambuild.command import Command
5 from ambuild.command import ShellCommand
6 from ambuild.command import SymlinkCommand
8 class ExtractDebugInfoCommand(Command):
9 def __init__(self, binary, outfile):
10 Command.__init__(self)
12 self.outfile = outfile
14 def run(self, runner, job):
15 if AMBuild.cache['debug']:
18 if not self.binary.NeedsRelink(self.outfile):
21 if AMBuild.target['platform'] == 'linux':
22 job.AddCommand(ShellCommand('objcopy --only-keep-debug ' + self.outfile + ' ' + self.outfile + '.dbg'))
23 job.AddCommand(ShellCommand('objcopy --strip-debug ' + self.outfile))
24 job.AddCommand(ShellCommand('objcopy --add-gnu-debuglink=' + os.path.basename(self.outfile) + '.dbg ' + self.outfile))
25 elif AMBuild.target['platform'] == 'darwin':
26 job.AddCommand(ShellCommand('dsymutil ' + self.outfile))
27 job.AddCommand(ShellCommand('strip -S ' + self.outfile))
31 self.compiler = Cpp.Compiler()
34 self.possibleSdks = { }
35 self.possibleSdks['ep1'] = {'sdk': 'HL2SDK', 'ext': '1.ep1', 'def': '1',
36 'name': 'EPISODEONE', 'platform': ['windows', 'linux'],
38 self.possibleSdks['ep2'] = {'sdk': 'HL2SDKOB', 'ext': '2.ep2', 'def': '3',
39 'name': 'ORANGEBOX', 'platform': ['windows', 'linux'],
41 self.possibleSdks['css'] = {'sdk': 'HL2SDKCSS', 'ext': '2.css', 'def': '6',
42 'name': 'CSS', 'platform': ['windows', 'linux', 'darwin'],
44 self.possibleSdks['ep2v'] = {'sdk': 'HL2SDKOBVALVE', 'ext': '2.ep2v', 'def': '7',
45 'name': 'ORANGEBOXVALVE', 'platform': ['windows', 'linux', 'darwin'],
46 'dir': 'hl2sdk-ob-valve'}
47 self.possibleSdks['l4d'] = {'sdk': 'HL2SDKL4D', 'ext': '2.l4d', 'def': '8',
48 'name': 'LEFT4DEAD', 'platform': ['windows', 'linux', 'darwin'],
50 self.possibleSdks['l4d2'] = {'sdk': 'HL2SDKL4D2', 'ext': '2.l4d2', 'def': '9',
51 'name': 'LEFT4DEAD2', 'platform': ['windows', 'linux', 'darwin'],
53 self.possibleSdks['darkm'] = {'sdk': 'HL2SDK-DARKM', 'ext': '2.darkm', 'def': '2',
54 'name': 'DARKMESSIAH', 'platform': ['windows'],
55 'dir': 'hl2sdk-darkm'}
56 self.possibleSdks['swarm'] = {'sdk': 'HL2SDK-SWARM', 'ext': '2.swarm', 'def': '10',
57 'name': 'ALIENSWARM', 'platform': ['windows'],
58 'dir': 'hl2sdk-swarm'}
59 self.possibleSdks['bgt'] = {'sdk': 'HL2SDK-BGT', 'ext': '2.bgt', 'def': '4',
60 'name': 'BLOODYGOODTIME', 'platform': ['windows'],
62 self.possibleSdks['eye'] = {'sdk': 'HL2SDK-EYE', 'ext': '2.eye', 'def': '5',
63 'name': 'EYE', 'platform': ['windows'],
65 self.possibleSdks['csgo'] = {'sdk': 'HL2SDKCSGO', 'ext': '2.csgo', 'def': '12',
66 'name': 'CSGO', 'platform': ['windows', 'linux', 'darwin'],
68 self.possibleSdks['dota'] = {'sdk': 'HL2SDKDOTA', 'ext': '2.dota', 'def': '13',
69 'name': 'DOTA', 'platform': ['windows'],
71 # self.possibleSdks['portal2'] = {'sdk': 'HL2SDK-PORTAL2', 'ext': '2.portal2', 'def': '11',
72 # 'name': 'PORTAL2', 'platform': ['windows'],
73 # 'dir': 'hl2sdk-portal2'}
77 if AMBuild.mode == 'config':
79 self.compiler.DetectAll(AMBuild)
81 self.hasMySql = AMBuild.options.hasMySql
82 AMBuild.cache.CacheVariable('hasMySql', self.hasMySql)
84 AMBuild.cache.CacheVariable('debug', AMBuild.options.debug)
86 #Environment variable paths to external headers
87 envvars = { 'MMSOURCE19': 'mmsource-1.9', 'MYSQL5': 'mysql-5.0' }
89 #Look for Metamod:Source and MySQL if not disabled
91 if not self.hasMySql and env == 'MYSQL5':
93 path = self.ResolveEnvPath(env, envvars[env])
95 raise Exception('Could not find a valid path for {0}'.format(env))
96 AMBuild.cache.CacheVariable(env, path)
98 #Look for SDK directories
99 for sdk in self.possibleSdks:
100 #Get list of SDKs to build against or 'all' or 'present'
101 sdkList = AMBuild.options.sdks.split(',')
102 #Build against all supported SDKs?
103 useAll = sdkList[0] == 'all'
104 #Build against supported SDKs that exist?
105 usePresent = sdkList[0] == 'present'
107 info = self.possibleSdks[sdk]
108 if AMBuild.target['platform'] in info['platform']:
111 sdkPath = self.ResolveEnvPath(env, dir)
113 if useAll or sdk in sdkList:
114 raise Exception('Could not find a valid path for {0}'.format(env))
117 if useAll or usePresent or sdk in sdkList:
118 self.sdkInfo[sdk] = info
119 AMBuild.cache.CacheVariable(env, sdkPath)
121 if len(self.sdkInfo) < 1:
122 raise Exception('At least one SDK must be available.')
124 AMBuild.cache.CacheVariable('sdkInfo', self.sdkInfo)
127 cxx = self.compiler.cxx
128 if isinstance(cxx, Cpp.CompatGCC):
129 if isinstance(cxx, Cpp.GCC):
131 elif isinstance(cxx, Cpp.Clang):
132 self.vendor = 'clang'
133 self.compiler.AddToListVar('CDEFINES', 'stricmp=strcasecmp')
134 self.compiler.AddToListVar('CDEFINES', '_stricmp=strcasecmp')
135 self.compiler.AddToListVar('CDEFINES', '_snprintf=snprintf')
136 self.compiler.AddToListVar('CDEFINES', '_vsnprintf=vsnprintf')
137 self.compiler.AddToListVar('CFLAGS', '-pipe')
138 self.compiler.AddToListVar('CFLAGS', '-fno-strict-aliasing')
139 if (self.vendor == 'gcc' and cxx.majorVersion >= 4) or self.vendor == 'clang':
140 self.compiler.AddToListVar('CFLAGS', '-fvisibility=hidden')
141 self.compiler.AddToListVar('CXXFLAGS', '-fvisibility-inlines-hidden')
142 self.compiler.AddToListVar('CFLAGS', '-Wall')
143 self.compiler.AddToListVar('CFLAGS', '-Werror')
144 self.compiler.AddToListVar('CFLAGS', '-Wno-uninitialized')
145 self.compiler.AddToListVar('CFLAGS', '-Wno-unused')
146 self.compiler.AddToListVar('CFLAGS', '-Wno-switch')
147 self.compiler.AddToListVar('CFLAGS', '-msse')
148 self.compiler.AddToListVar('CFLAGS', '-g3')
149 self.compiler.AddToListVar('CFLAGS', '-m32')
150 self.compiler.AddToListVar('POSTLINKFLAGS', '-m32')
151 self.compiler.AddToListVar('CXXFLAGS', '-fno-exceptions')
152 self.compiler.AddToListVar('CXXFLAGS', '-fno-threadsafe-statics')
153 self.compiler.AddToListVar('CXXFLAGS', '-Wno-non-virtual-dtor')
154 self.compiler.AddToListVar('CXXFLAGS', '-Wno-overloaded-virtual')
155 if (self.vendor == 'gcc' and cxx.majorVersion >= 4 and cxx.minorVersion >= 7) or \
156 (self.vendor == 'clang' and cxx.majorVersion >= 3):
157 self.compiler.AddToListVar('CXXFLAGS', '-Wno-delete-non-virtual-dtor')
158 self.compiler.AddToListVar('CDEFINES', 'HAVE_STDINT_H')
159 self.compiler.AddToListVar('CDEFINES', 'GNUC')
160 if self.vendor == 'gcc':
161 self.compiler.AddToListVar('CFLAGS', '-mfpmath=sse')
162 elif isinstance(cxx, Cpp.MSVC):
164 if AMBuild.options.debug == '1':
165 self.compiler.AddToListVar('CFLAGS', '/MTd')
166 self.compiler.AddToListVar('POSTLINKFLAGS', '/NODEFAULTLIB:libcmt')
168 self.compiler.AddToListVar('CFLAGS', '/MT')
169 self.compiler.AddToListVar('CDEFINES', '_CRT_SECURE_NO_DEPRECATE')
170 self.compiler.AddToListVar('CDEFINES', '_CRT_SECURE_NO_WARNINGS')
171 self.compiler.AddToListVar('CDEFINES', '_CRT_NONSTDC_NO_DEPRECATE')
172 self.compiler.AddToListVar('CXXFLAGS', '/EHsc')
173 self.compiler.AddToListVar('CXXFLAGS', '/GR-')
174 self.compiler.AddToListVar('CFLAGS', '/W3')
175 self.compiler.AddToListVar('CFLAGS', '/nologo')
176 self.compiler.AddToListVar('CFLAGS', '/Zi')
177 self.compiler.AddToListVar('CXXFLAGS', '/TP')
178 self.compiler.AddToListVar('POSTLINKFLAGS', '/DEBUG')
179 self.compiler.AddToListVar('POSTLINKFLAGS', '/MACHINE:X86')
180 self.compiler.AddToListVar('POSTLINKFLAGS', '/SUBSYSTEM:WINDOWS')
181 self.compiler.AddToListVar('POSTLINKFLAGS', 'kernel32.lib')
182 self.compiler.AddToListVar('POSTLINKFLAGS', 'user32.lib')
183 self.compiler.AddToListVar('POSTLINKFLAGS', 'gdi32.lib')
184 self.compiler.AddToListVar('POSTLINKFLAGS', 'winspool.lib')
185 self.compiler.AddToListVar('POSTLINKFLAGS', 'comdlg32.lib')
186 self.compiler.AddToListVar('POSTLINKFLAGS', 'advapi32.lib')
187 self.compiler.AddToListVar('POSTLINKFLAGS', 'shell32.lib')
188 self.compiler.AddToListVar('POSTLINKFLAGS', 'ole32.lib')
189 self.compiler.AddToListVar('POSTLINKFLAGS', 'oleaut32.lib')
190 self.compiler.AddToListVar('POSTLINKFLAGS', 'uuid.lib')
191 self.compiler.AddToListVar('POSTLINKFLAGS', 'odbc32.lib')
192 self.compiler.AddToListVar('POSTLINKFLAGS', 'odbccp32.lib')
195 if AMBuild.options.opt == '1':
196 self.compiler.AddToListVar('CDEFINES', 'NDEBUG')
197 if self.vendor == 'gcc' or self.vendor == 'clang':
198 self.compiler.AddToListVar('CFLAGS', '-O3')
199 elif self.vendor == 'msvc':
200 self.compiler.AddToListVar('CFLAGS', '/Ox')
201 self.compiler.AddToListVar('POSTLINKFLAGS', '/OPT:ICF')
202 self.compiler.AddToListVar('POSTLINKFLAGS', '/OPT:REF')
205 if AMBuild.options.debug == '1':
206 self.compiler.AddToListVar('CDEFINES', 'DEBUG')
207 self.compiler.AddToListVar('CDEFINES', '_DEBUG')
208 if self.vendor == 'msvc':
209 self.compiler.AddToListVar('CFLAGS', '/Od')
210 self.compiler.AddToListVar('CFLAGS', '/RTC1')
212 #This needs to be after our optimization flags which could otherwise disable it.
213 if self.vendor == 'msvc':
214 # Don't omit frame pointer
215 self.compiler.AddToListVar('CFLAGS', '/Oy-')
218 if AMBuild.target['platform'] == 'linux':
219 self.compiler.AddToListVar('CDEFINES', '_LINUX')
220 self.compiler.AddToListVar('CDEFINES', 'POSIX')
221 if self.vendor == 'gcc':
222 self.compiler.AddToListVar('POSTLINKFLAGS', '-static-libgcc')
223 if self.vendor == 'clang':
224 self.compiler.AddToListVar('POSTLINKFLAGS', '-lgcc_eh')
225 elif AMBuild.target['platform'] == 'darwin':
226 self.compiler.AddToListVar('CDEFINES', 'OSX')
227 self.compiler.AddToListVar('CDEFINES', '_OSX')
228 self.compiler.AddToListVar('CDEFINES', 'POSIX')
229 self.compiler.AddToListVar('POSTLINKFLAGS', '-mmacosx-version-min=10.5')
230 self.compiler.AddToListVar('POSTLINKFLAGS', ['-arch', 'i386'])
231 self.compiler.AddToListVar('POSTLINKFLAGS', '-lstdc++')
233 # For OS X dylib versioning
235 productFile = open(os.path.join(AMBuild.sourceFolder, 'product.version'), 'r')
236 productContents = productFile.read()
238 m = re.match('(\d+)\.(\d+)\.(\d+).*', productContents)
240 self.version = '1.0.0'
242 major, minor, release = m.groups()
243 self.version = '{0}.{1}.{2}'.format(major, minor, release)
244 AMBuild.cache.CacheVariable('version', self.version)
245 elif AMBuild.target['platform'] == 'windows':
246 self.compiler.AddToListVar('CDEFINES', 'WIN32')
247 self.compiler.AddToListVar('CDEFINES', '_WINDOWS')
250 self.compiler.AddToListVar('CDEFINES', 'SOURCEMOD_BUILD')
251 self.compiler.AddToListVar('CDEFINES', 'SM_GENERATED_BUILD')
252 self.compiler.AddToListVar('CINCLUDES',
253 os.path.join(AMBuild.outputFolder, 'includes'))
254 self.compiler.ToConfig(AMBuild, 'compiler')
255 AMBuild.cache.CacheVariable('vendor', self.vendor)
257 AMBuild.cache.CacheVariable('targetMap', self.targetMap)
259 self.sdkInfo = AMBuild.cache['sdkInfo']
260 self.compiler.FromConfig(AMBuild, 'compiler')
261 self.targetMap = AMBuild.cache['targetMap']
262 self.hasMySql = AMBuild.cache['hasMySql']
264 if AMBuild.target['platform'] == 'windows':
265 self.compiler.AddToListVar('RCINCLUDES', os.path.join(AMBuild.sourceFolder, 'public'))
266 self.compiler.AddToListVar('RCINCLUDES',
267 os.path.join(AMBuild.outputFolder, 'includes'))
268 self.mmsPath = AMBuild.cache['MMSOURCE19']
270 def DefaultCompiler(self):
271 return self.compiler.Clone()
273 def JobMatters(self, jobname):
274 file = sys._getframe().f_code.co_filename
275 if AMBuild.mode == 'config':
276 self.targetMap[jobname] = file
278 if len(AMBuild.args) == 0:
280 if not jobname in AMBuild.args:
283 def DefaultExtCompiler(self, path):
284 compiler = self.DefaultCompiler()
285 compiler['CXXINCLUDES'].append(os.path.join(AMBuild.sourceFolder, path))
286 compiler['CXXINCLUDES'].append(os.path.join(AMBuild.sourceFolder, path, 'sdk'))
287 compiler['CXXINCLUDES'].append(os.path.join(AMBuild.sourceFolder, 'public'))
288 compiler['CXXINCLUDES'].append(os.path.join(AMBuild.sourceFolder, 'public', 'extensions'))
289 compiler['CXXINCLUDES'].append(os.path.join(AMBuild.sourceFolder, 'public', 'sourcepawn'))
292 def AutoVersion(self, folder, binary):
293 if AMBuild.target['platform'] == 'windows':
294 env = {'RCDEFINES': ['BINARY_NAME="' + binary.binaryFile + '"', 'SM_GENERATED_BUILD']}
295 binary.AddResourceFile(os.path.join(folder, 'version.rc' ), env)
296 elif AMBuild.target['platform'] == 'darwin' and isinstance(binary, Cpp.LibraryBuilder):
297 binary.compiler['POSTLINKFLAGS'].extend(['-compatibility_version', '1.0.0'])
298 binary.compiler['POSTLINKFLAGS'].extend(['-current_version', AMBuild.cache['version']])
302 def ExtractDebugInfo(self, job, binary):
303 src = os.path.join('..', AMBuild.outputFolder, job.workFolder, binary.binaryFile)
304 job.AddCommand(ExtractDebugInfoCommand(binary, src))
306 def PreSetupHL2Job(self, job, builder, sdk):
307 info = self.sdkInfo[sdk]
308 sdkPath = AMBuild.cache[info['sdk']]
309 if AMBuild.target['platform'] == 'linux':
311 staticLibs = os.path.join(sdkPath, 'linux_sdk')
313 staticLibs = os.path.join(sdkPath, 'lib', 'linux')
314 workFolder = os.path.join(AMBuild.outputFolder, job.workFolder)
315 if sdk in ['ep2v', 'css', 'l4d2']:
316 libs = ['tier1_i486.a', 'mathlib_i486.a', 'libvstdlib_srv.so', 'libtier0_srv.so']
318 link = os.path.join(workFolder, lib)
319 target = os.path.join(staticLibs, lib)
323 job.AddCommand(SymlinkCommand(link, target))
324 elif sdk in ['l4d', 'csgo']:
325 libs = ['tier1_i486.a', 'mathlib_i486.a', 'libvstdlib.so', 'libtier0.so']
327 libs.append('interfaces_i486.a')
329 link = os.path.join(workFolder, lib)
330 target = os.path.join(staticLibs, lib)
334 job.AddCommand(SymlinkCommand(link, target))
336 libs = ['tier1_i486.a', 'mathlib_i486.a', 'vstdlib_i486.so', 'tier0_i486.so']
338 link = os.path.join(workFolder, lib)
339 target = os.path.join(staticLibs, lib)
343 job.AddCommand(SymlinkCommand(link, target))
344 elif AMBuild.target['platform'] == 'darwin':
345 staticLibs = os.path.join(sdkPath, 'lib', 'mac')
346 workFolder = os.path.join(AMBuild.outputFolder, job.workFolder)
347 libs = ['tier1_i486.a', 'mathlib_i486.a', 'libvstdlib.dylib', 'libtier0.dylib']
349 libs.append('interfaces_i486.a')
351 link = os.path.join(workFolder, lib)
352 target = os.path.join(staticLibs, lib)
356 job.AddCommand(SymlinkCommand(link, target))
357 elif AMBuild.target['platform'] == 'windows':
358 libs = ['tier0', 'tier1', 'vstdlib', 'mathlib']
359 if sdk in ['swarm', 'csgo', 'dota']:
360 libs.append('interfaces')
362 libPath = os.path.join(sdkPath, 'lib', 'public', lib) + '.lib'
363 builder.RebuildIfNewer(libPath)
364 builder['POSTLINKFLAGS'].append(libPath)
366 def PostSetupHL2Job(self, job, builder, sdk):
367 if AMBuild.target['platform'] in ['linux', 'darwin']:
368 builder.AddObjectFiles(['tier1_i486.a', 'mathlib_i486.a'])
370 builder.AddObjectFiles(['interfaces_i486.a'])
372 def DefaultHL2Compiler(self, path, sdk, noLink = False, oldMms = '-legacy'):
373 compiler = self.DefaultExtCompiler(path)
379 compiler['CXXINCLUDES'].append(os.path.join(self.mmsPath, mms))
380 compiler['CXXINCLUDES'].append(os.path.join(self.mmsPath, mms, 'sourcehook'))
382 info = self.possibleSdks
383 compiler['CDEFINES'].extend(['SE_' + info[i]['name'] + '=' + info[i]['def'] for i in info])
385 # We don't build for Portal 2 (yet?, ever?), but using this define in code as
386 # it saves trouble if we ever need to
387 compiler['CDEFINES'].append('SE_PORTAL2=11')
389 paths = [['public'], ['public', 'engine'], ['public', 'mathlib'], ['public', 'vstdlib'],
390 ['public', 'tier0'], ['public', 'tier1']]
391 if sdk == 'ep1' or sdk == 'darkm':
392 paths.append(['public', 'dlls'])
393 paths.append(['game_shared'])
395 paths.append(['public', 'game', 'server'])
396 paths.append(['public', 'toolframework'])
397 paths.append(['game', 'shared'])
398 paths.append(['common'])
400 info = self.sdkInfo[sdk]
401 sdkPath = AMBuild.cache[info['sdk']]
403 compiler['CDEFINES'].append('SOURCE_ENGINE=' + info['def'])
405 if sdk in ['swarm','csgo','dota']:
406 if AMBuild.target['platform'] == 'windows':
407 compiler['CDEFINES'].extend(['COMPILER_MSVC', 'COMPILER_MSVC32'])
409 compiler['CDEFINES'].extend(['COMPILER_GCC'])
412 if AMBuild.target['platform'] == 'linux':
413 staticLibs = os.path.join(sdkPath, 'linux_sdk')
415 if AMBuild.target['platform'] == 'linux':
416 staticLibs = os.path.join(sdkPath, 'lib', 'linux')
417 elif AMBuild.target['platform'] == 'darwin':
418 staticLibs = os.path.join(sdkPath, 'lib', 'mac')
421 compiler['CXXINCLUDES'].append(os.path.join(sdkPath, *i))
424 if AMBuild.target['platform'] == 'linux':
425 compiler['POSTLINKFLAGS'][0:0] = ['-lm']
426 if sdk in ['ep2v', 'css', 'l4d2']:
427 compiler['POSTLINKFLAGS'][0:0] = ['libtier0_srv.so']
428 compiler['POSTLINKFLAGS'][0:0] = ['libvstdlib_srv.so']
429 elif sdk in ['l4d', 'csgo']:
430 compiler['POSTLINKFLAGS'][0:0] = ['libtier0.so']
431 compiler['POSTLINKFLAGS'][0:0] = ['libvstdlib.so']
433 compiler['POSTLINKFLAGS'][0:0] = ['tier0_i486.so']
434 compiler['POSTLINKFLAGS'][0:0] = ['vstdlib_i486.so']
435 elif AMBuild.target['platform'] == 'darwin':
436 compiler['POSTLINKFLAGS'][0:0] = ['libtier0.dylib']
437 compiler['POSTLINKFLAGS'][0:0] = ['libvstdlib.dylib']
441 def ResolveEnvPath(self, env, defaultDir):
442 if env in os.environ:
443 path = os.environ[env]
444 if os.path.isdir(path):
449 while head != None and head != oldhead:
450 path = os.path.join(head, defaultDir)
451 if os.path.isdir(path):
454 head, tail = os.path.split(head)
462 AMBuild.Include(os.path.join('tools', 'buildbot', 'Versioning'), globals)
465 ['loader', 'AMBuilder'],
466 ['core', 'AMBuilder'],
467 ['core', 'logic', 'AMBuilder'],
468 ['extensions', 'bintools', 'AMBuilder'],
469 ['extensions', 'clientprefs', 'AMBuilder'],
470 ['extensions', 'cstrike', 'AMBuilder'],
471 ['extensions', 'curl', 'AMBuilder'],
472 ['extensions', 'geoip', 'AMBuilder'],
473 ['extensions', 'mysql', 'AMBuilder'],
474 ['extensions', 'sdkhooks', 'AMBuilder'],
475 ['extensions', 'sdktools', 'AMBuilder'],
476 ['extensions', 'topmenus', 'AMBuilder'],
477 ['extensions', 'updater', 'AMBuilder'],
478 ['extensions', 'sqlite', 'AMBuilder'],
479 ['extensions', 'regex', 'AMBuilder'],
480 ['extensions', 'tf2', 'AMBuilder'],
481 ['sourcepawn', 'jit', 'AMBuilder'],
482 ['sourcepawn', 'compiler', 'AMBuilder'],
483 ['plugins', 'AMBuilder'],
484 ['tools', 'buildbot', 'PackageScript'],
485 ['tools', 'buildbot', 'BreakpadSymbols']
489 FileList.remove(['extensions', 'mysql', 'AMBuilder'])
491 for parts in FileList:
492 AMBuild.Include(os.path.join(*parts), globals)