scons: Restrict whole program optimization to release builds.
[mesa.git] / scons / gallium.py
1 """gallium
2
3 Frontend-tool for Gallium3D architecture.
4
5 """
6
7 #
8 # Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
9 # All Rights Reserved.
10 #
11 # Permission is hereby granted, free of charge, to any person obtaining a
12 # copy of this software and associated documentation files (the
13 # "Software"), to deal in the Software without restriction, including
14 # without limitation the rights to use, copy, modify, merge, publish,
15 # distribute, sub license, and/or sell copies of the Software, and to
16 # permit persons to whom the Software is furnished to do so, subject to
17 # the following conditions:
18 #
19 # The above copyright notice and this permission notice (including the
20 # next paragraph) shall be included in all copies or substantial portions
21 # of the Software.
22 #
23 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
26 # IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
27 # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
28 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
29 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 #
31
32
33 import distutils.version
34 import os
35 import os.path
36 import re
37 import subprocess
38
39 import SCons.Action
40 import SCons.Builder
41 import SCons.Scanner
42
43
44 def symlink(target, source, env):
45 target = str(target[0])
46 source = str(source[0])
47 if os.path.islink(target) or os.path.exists(target):
48 os.remove(target)
49 os.symlink(os.path.basename(source), target)
50
51 def install(env, source, subdir):
52 target_dir = os.path.join(env.Dir('#.').srcnode().abspath, env['build_dir'], subdir)
53 return env.Install(target_dir, source)
54
55 def install_program(env, source):
56 return install(env, source, 'bin')
57
58 def install_shared_library(env, sources, version = ()):
59 targets = []
60 install_dir = os.path.join(env.Dir('#.').srcnode().abspath, env['build_dir'])
61 version = tuple(map(str, version))
62 if env['SHLIBSUFFIX'] == '.dll':
63 dlls = env.FindIxes(sources, 'SHLIBPREFIX', 'SHLIBSUFFIX')
64 targets += install(env, dlls, 'bin')
65 libs = env.FindIxes(sources, 'LIBPREFIX', 'LIBSUFFIX')
66 targets += install(env, libs, 'lib')
67 else:
68 for source in sources:
69 target_dir = os.path.join(install_dir, 'lib')
70 target_name = '.'.join((str(source),) + version)
71 last = env.InstallAs(os.path.join(target_dir, target_name), source)
72 targets += last
73 while len(version):
74 version = version[:-1]
75 target_name = '.'.join((str(source),) + version)
76 action = SCons.Action.Action(symlink, "$TARGET -> $SOURCE")
77 last = env.Command(os.path.join(target_dir, target_name), last, action)
78 targets += last
79 return targets
80
81
82 def createInstallMethods(env):
83 env.AddMethod(install_program, 'InstallProgram')
84 env.AddMethod(install_shared_library, 'InstallSharedLibrary')
85
86
87 def num_jobs():
88 try:
89 return int(os.environ['NUMBER_OF_PROCESSORS'])
90 except (ValueError, KeyError):
91 pass
92
93 try:
94 return os.sysconf('SC_NPROCESSORS_ONLN')
95 except (ValueError, OSError, AttributeError):
96 pass
97
98 try:
99 return int(os.popen2("sysctl -n hw.ncpu")[1].read())
100 except ValueError:
101 pass
102
103 return 1
104
105
106 def pkg_config_modules(env, name, modules):
107 '''Simple wrapper for pkg-config.'''
108
109 env[name] = False
110
111 if env['platform'] == 'windows':
112 return
113
114 if not env.Detect('pkg-config'):
115 return
116
117 if subprocess.call(["pkg-config", "--exists", ' '.join(modules)]) != 0:
118 return
119
120 # Put -I and -L flags directly into the environment, as these don't affect
121 # the compilation of targets that do not use them
122 try:
123 env.ParseConfig('pkg-config --cflags-only-I --libs-only-L ' + ' '.join(modules))
124 except OSError:
125 return
126
127 # Other flags may affect the compilation of unrelated targets, so store
128 # them with a prefix, (e.g., XXX_CFLAGS, XXX_LIBS, etc)
129 try:
130 flags = env.ParseFlags('!pkg-config --cflags-only-other --libs-only-l --libs-only-other ' + ' '.join(modules))
131 except OSError:
132 return
133 prefix = name.upper() + '_'
134 for flag_name, flag_value in flags.iteritems():
135 env[prefix + flag_name] = flag_value
136
137 env[name] = True
138
139
140
141 def generate(env):
142 """Common environment generation code"""
143
144 # Toolchain
145 platform = env['platform']
146 if env['toolchain'] == 'default':
147 if platform == 'winddk':
148 env['toolchain'] = 'winddk'
149 elif platform == 'wince':
150 env['toolchain'] = 'wcesdk'
151 env.Tool(env['toolchain'])
152
153 # Allow override compiler and specify additional flags from environment
154 if os.environ.has_key('CC'):
155 env['CC'] = os.environ['CC']
156 # Update CCVERSION to match
157 pipe = SCons.Action._subproc(env, [env['CC'], '--version'],
158 stdin = 'devnull',
159 stderr = 'devnull',
160 stdout = subprocess.PIPE)
161 if pipe.wait() == 0:
162 line = pipe.stdout.readline()
163 match = re.search(r'[0-9]+(\.[0-9]+)+', line)
164 if match:
165 env['CCVERSION'] = match.group(0)
166 if os.environ.has_key('CFLAGS'):
167 env['CCFLAGS'] += SCons.Util.CLVar(os.environ['CFLAGS'])
168 if os.environ.has_key('CXX'):
169 env['CXX'] = os.environ['CXX']
170 if os.environ.has_key('CXXFLAGS'):
171 env['CXXFLAGS'] += SCons.Util.CLVar(os.environ['CXXFLAGS'])
172 if os.environ.has_key('LDFLAGS'):
173 env['LINKFLAGS'] += SCons.Util.CLVar(os.environ['LDFLAGS'])
174
175 env['gcc'] = 'gcc' in os.path.basename(env['CC']).split('-')
176 env['msvc'] = env['CC'] == 'cl'
177
178 # shortcuts
179 machine = env['machine']
180 platform = env['platform']
181 x86 = env['machine'] == 'x86'
182 ppc = env['machine'] == 'ppc'
183 gcc = env['gcc']
184 msvc = env['msvc']
185
186 # Backwards compatability with the debug= profile= options
187 if env['build'] == 'debug':
188 if not env['debug']:
189 print 'scons: warning: debug option is deprecated and will be removed eventually; use instead'
190 print
191 print ' scons build=release'
192 print
193 env['build'] = 'release'
194 if env['profile']:
195 print 'scons: warning: profile option is deprecated and will be removed eventually; use instead'
196 print
197 print ' scons build=profile'
198 print
199 env['build'] = 'profile'
200 if False:
201 # Enforce SConscripts to use the new build variable
202 env.popitem('debug')
203 env.popitem('profile')
204 else:
205 # Backwards portability with older sconscripts
206 if env['build'] in ('debug', 'checked'):
207 env['debug'] = True
208 env['profile'] = False
209 if env['build'] == 'profile':
210 env['debug'] = False
211 env['profile'] = True
212 if env['build'] == 'release':
213 env['debug'] = False
214 env['profile'] = False
215
216 # Put build output in a separate dir, which depends on the current
217 # configuration. See also http://www.scons.org/wiki/AdvancedBuildExample
218 build_topdir = 'build'
219 build_subdir = env['platform']
220 if env['machine'] != 'generic':
221 build_subdir += '-' + env['machine']
222 if env['build'] != 'release':
223 build_subdir += '-' + env['build']
224 build_dir = os.path.join(build_topdir, build_subdir)
225 # Place the .sconsign file in the build dir too, to avoid issues with
226 # different scons versions building the same source file
227 env['build_dir'] = build_dir
228 env.SConsignFile(os.path.join(build_dir, '.sconsign'))
229 if 'SCONS_CACHE_DIR' in os.environ:
230 print 'scons: Using build cache in %s.' % (os.environ['SCONS_CACHE_DIR'],)
231 env.CacheDir(os.environ['SCONS_CACHE_DIR'])
232 env['CONFIGUREDIR'] = os.path.join(build_dir, 'conf')
233 env['CONFIGURELOG'] = os.path.join(os.path.abspath(build_dir), 'config.log')
234
235 # Parallel build
236 if env.GetOption('num_jobs') <= 1:
237 env.SetOption('num_jobs', num_jobs())
238
239 env.Decider('MD5-timestamp')
240 env.SetOption('max_drift', 60)
241
242 # C preprocessor options
243 cppdefines = []
244 if env['build'] in ('debug', 'checked'):
245 cppdefines += ['DEBUG']
246 else:
247 cppdefines += ['NDEBUG']
248 if env['build'] == 'profile':
249 cppdefines += ['PROFILE']
250 if platform == 'windows':
251 cppdefines += [
252 'WIN32',
253 '_WINDOWS',
254 #'_UNICODE',
255 #'UNICODE',
256 # http://msdn.microsoft.com/en-us/library/aa383745.aspx
257 ('_WIN32_WINNT', '0x0601'),
258 ('WINVER', '0x0601'),
259 ]
260 if msvc and env['toolchain'] != 'winddk':
261 cppdefines += [
262 'VC_EXTRALEAN',
263 '_USE_MATH_DEFINES',
264 '_CRT_SECURE_NO_WARNINGS',
265 '_CRT_SECURE_NO_DEPRECATE',
266 '_SCL_SECURE_NO_WARNINGS',
267 '_SCL_SECURE_NO_DEPRECATE',
268 ]
269 if env['build'] in ('debug', 'checked'):
270 cppdefines += ['_DEBUG']
271 if env['toolchain'] == 'winddk':
272 # Mimic WINDDK's builtin flags. See also:
273 # - WINDDK's bin/makefile.new i386mk.inc for more info.
274 # - buildchk_wxp_x86.log files, generated by the WINDDK's build
275 # - http://alter.org.ua/docs/nt_kernel/vc8_proj/
276 if machine == 'x86':
277 cppdefines += ['_X86_', 'i386']
278 if machine == 'x86_64':
279 cppdefines += ['_AMD64_', 'AMD64']
280 if platform == 'winddk':
281 cppdefines += [
282 'STD_CALL',
283 ('CONDITION_HANDLING', '1'),
284 ('NT_INST', '0'),
285 ('WIN32', '100'),
286 ('_NT1X_', '100'),
287 ('WINNT', '1'),
288 ('_WIN32_WINNT', '0x0501'), # minimum required OS version
289 ('WINVER', '0x0501'),
290 ('_WIN32_IE', '0x0603'),
291 ('WIN32_LEAN_AND_MEAN', '1'),
292 ('DEVL', '1'),
293 ('__BUILDMACHINE__', 'WinDDK'),
294 ('FPO', '0'),
295 ]
296 if env['build'] in ('debug', 'checked'):
297 cppdefines += [('DBG', 1)]
298 if platform == 'wince':
299 cppdefines += [
300 '_CRT_SECURE_NO_DEPRECATE',
301 '_USE_32BIT_TIME_T',
302 'UNICODE',
303 '_UNICODE',
304 ('UNDER_CE', '600'),
305 ('_WIN32_WCE', '0x600'),
306 'WINCEOEM',
307 'WINCEINTERNAL',
308 'WIN32',
309 'STRICT',
310 'x86',
311 '_X86_',
312 'INTERNATIONAL',
313 ('INTLMSG_CODEPAGE', '1252'),
314 ]
315 if platform == 'windows':
316 cppdefines += ['PIPE_SUBSYSTEM_WINDOWS_USER']
317 if platform == 'winddk':
318 cppdefines += ['PIPE_SUBSYSTEM_WINDOWS_DISPLAY']
319 if platform == 'wince':
320 cppdefines += ['PIPE_SUBSYSTEM_WINDOWS_CE']
321 cppdefines += ['PIPE_SUBSYSTEM_WINDOWS_CE_OGL']
322 if platform == 'embedded':
323 cppdefines += ['PIPE_OS_EMBEDDED']
324 env.Append(CPPDEFINES = cppdefines)
325
326 # C compiler options
327 cflags = [] # C
328 cxxflags = [] # C++
329 ccflags = [] # C & C++
330 if gcc:
331 ccversion = env['CCVERSION']
332 if env['build'] == 'debug':
333 ccflags += ['-O0']
334 elif ccversion.startswith('4.2.'):
335 # gcc 4.2.x optimizer is broken
336 print "warning: gcc 4.2.x optimizer is broken -- disabling optimizations"
337 ccflags += ['-O0']
338 else:
339 ccflags += ['-O3']
340 ccflags += ['-g3']
341 if env['build'] in ('checked', 'profile'):
342 # See http://code.google.com/p/jrfonseca/wiki/Gprof2Dot#Which_options_should_I_pass_to_gcc_when_compiling_for_profiling?
343 ccflags += [
344 '-fno-omit-frame-pointer',
345 '-fno-optimize-sibling-calls',
346 ]
347 if env['machine'] == 'x86':
348 ccflags += [
349 '-m32',
350 #'-march=pentium4',
351 ]
352 if distutils.version.LooseVersion(ccversion) >= distutils.version.LooseVersion('4.2'):
353 # NOTE: We need to ensure stack is realigned given that we
354 # produce shared objects, and have no control over the stack
355 # alignment policy of the application. Therefore we need
356 # -mstackrealign ore -mincoming-stack-boundary=2.
357 #
358 # XXX: We could have SSE without -mstackrealign if we always used
359 # __attribute__((force_align_arg_pointer)), but that's not
360 # always the case.
361 ccflags += [
362 '-mstackrealign', # ensure stack is aligned
363 '-mmmx', '-msse', '-msse2', # enable SIMD intrinsics
364 #'-mfpmath=sse',
365 ]
366 if platform in ['windows', 'darwin']:
367 # Workaround http://gcc.gnu.org/bugzilla/show_bug.cgi?id=37216
368 ccflags += ['-fno-common']
369 if env['machine'] == 'x86_64':
370 ccflags += ['-m64']
371 if platform == 'darwin':
372 ccflags += ['-fno-common']
373 # See also:
374 # - http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
375 ccflags += [
376 '-Wall',
377 '-Wno-long-long',
378 '-ffast-math',
379 '-fmessage-length=0', # be nice to Eclipse
380 ]
381 cflags += [
382 '-Wmissing-prototypes',
383 '-std=gnu99',
384 ]
385 if distutils.version.LooseVersion(ccversion) >= distutils.version.LooseVersion('4.0'):
386 ccflags += [
387 '-Wmissing-field-initializers',
388 ]
389 if distutils.version.LooseVersion(ccversion) >= distutils.version.LooseVersion('4.2'):
390 ccflags += [
391 '-Werror=pointer-arith',
392 ]
393 cflags += [
394 '-Werror=declaration-after-statement',
395 ]
396 if msvc:
397 # See also:
398 # - http://msdn.microsoft.com/en-us/library/19z1t1wy.aspx
399 # - cl /?
400 if env['build'] == 'debug':
401 ccflags += [
402 '/Od', # disable optimizations
403 '/Oi', # enable intrinsic functions
404 '/Oy-', # disable frame pointer omission
405 ]
406 else:
407 ccflags += [
408 '/O2', # optimize for speed
409 ]
410 if env['build'] == 'release':
411 ccflags += [
412 '/GL', # enable whole program optimization
413 ]
414 else:
415 ccflags += [
416 '/GL-', # disable whole program optimization
417 ]
418 ccflags += [
419 '/fp:fast', # fast floating point
420 '/W3', # warning level
421 #'/Wp64', # enable 64 bit porting warnings
422 ]
423 if env['machine'] == 'x86':
424 ccflags += [
425 #'/arch:SSE2', # use the SSE2 instructions
426 ]
427 if platform == 'windows':
428 ccflags += [
429 # TODO
430 ]
431 if platform == 'winddk':
432 ccflags += [
433 '/Zl', # omit default library name in .OBJ
434 '/Zp8', # 8bytes struct member alignment
435 '/Gy', # separate functions for linker
436 '/Gm-', # disable minimal rebuild
437 '/WX', # treat warnings as errors
438 '/Gz', # __stdcall Calling convention
439 '/GX-', # disable C++ EH
440 '/GR-', # disable C++ RTTI
441 '/GF', # enable read-only string pooling
442 '/G6', # optimize for PPro, P-II, P-III
443 '/Ze', # enable extensions
444 '/Gi-', # disable incremental compilation
445 '/QIfdiv-', # disable Pentium FDIV fix
446 '/hotpatch', # prepares an image for hotpatching.
447 #'/Z7', #enable old-style debug info
448 ]
449 if platform == 'wince':
450 # See also C:\WINCE600\public\common\oak\misc\makefile.def
451 ccflags += [
452 '/Zl', # omit default library name in .OBJ
453 '/GF', # enable read-only string pooling
454 '/GR-', # disable C++ RTTI
455 '/GS', # enable security checks
456 # Allow disabling language conformance to maintain backward compat
457 #'/Zc:wchar_t-', # don't force wchar_t as native type, instead of typedef
458 #'/Zc:forScope-', # don't enforce Standard C++ for scoping rules
459 #'/wd4867',
460 #'/wd4430',
461 #'/MT',
462 #'/U_MT',
463 ]
464 # Automatic pdb generation
465 # See http://scons.tigris.org/issues/show_bug.cgi?id=1656
466 env.EnsureSConsVersion(0, 98, 0)
467 env['PDB'] = '${TARGET.base}.pdb'
468 env.Append(CCFLAGS = ccflags)
469 env.Append(CFLAGS = cflags)
470 env.Append(CXXFLAGS = cxxflags)
471
472 if env['platform'] == 'windows' and msvc:
473 # Choose the appropriate MSVC CRT
474 # http://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx
475 if env['build'] in ('debug', 'checked'):
476 env.Append(CCFLAGS = ['/MTd'])
477 env.Append(SHCCFLAGS = ['/LDd'])
478 else:
479 env.Append(CCFLAGS = ['/MT'])
480 env.Append(SHCCFLAGS = ['/LD'])
481
482 # Assembler options
483 if gcc:
484 if env['machine'] == 'x86':
485 env.Append(ASFLAGS = ['-m32'])
486 if env['machine'] == 'x86_64':
487 env.Append(ASFLAGS = ['-m64'])
488
489 # Linker options
490 linkflags = []
491 shlinkflags = []
492 if gcc:
493 if env['machine'] == 'x86':
494 linkflags += ['-m32']
495 if env['machine'] == 'x86_64':
496 linkflags += ['-m64']
497 if env['platform'] not in ('darwin'):
498 shlinkflags += [
499 '-Wl,-Bsymbolic',
500 ]
501 # Handle circular dependencies in the libraries
502 if env['platform'] in ('darwin'):
503 pass
504 else:
505 env['_LIBFLAGS'] = '-Wl,--start-group ' + env['_LIBFLAGS'] + ' -Wl,--end-group'
506 if msvc:
507 if env['build'] == 'release':
508 # enable Link-time Code Generation
509 linkflags += ['/LTCG']
510 env.Append(ARFLAGS = ['/LTCG'])
511 if platform == 'windows' and msvc:
512 # See also:
513 # - http://msdn2.microsoft.com/en-us/library/y0zzbyt4.aspx
514 linkflags += [
515 '/fixed:no',
516 '/incremental:no',
517 ]
518 if platform == 'winddk':
519 linkflags += [
520 '/merge:_PAGE=PAGE',
521 '/merge:_TEXT=.text',
522 '/section:INIT,d',
523 '/opt:ref',
524 '/opt:icf',
525 '/ignore:4198,4010,4037,4039,4065,4070,4078,4087,4089,4221',
526 '/incremental:no',
527 '/fullbuild',
528 '/release',
529 '/nodefaultlib',
530 '/wx',
531 '/debug',
532 '/debugtype:cv',
533 '/version:5.1',
534 '/osversion:5.1',
535 '/functionpadmin:5',
536 '/safeseh',
537 '/pdbcompress',
538 '/stack:0x40000,0x1000',
539 '/driver',
540 '/align:0x80',
541 '/subsystem:native,5.01',
542 '/base:0x10000',
543
544 '/entry:DrvEnableDriver',
545 ]
546 if env['build'] != 'release':
547 linkflags += [
548 '/MAP', # http://msdn.microsoft.com/en-us/library/k7xkk3e2.aspx
549 ]
550 if platform == 'wince':
551 linkflags += [
552 '/nodefaultlib',
553 #'/incremental:no',
554 #'/fullbuild',
555 '/entry:_DllMainCRTStartup',
556 ]
557 env.Append(LINKFLAGS = linkflags)
558 env.Append(SHLINKFLAGS = shlinkflags)
559
560 # We have C++ in several libraries, so always link with the C++ compiler
561 if env['gcc']:
562 env['LINK'] = env['CXX']
563
564 # Default libs
565 env.Append(LIBS = [])
566
567 # Load tools
568 if env['llvm']:
569 env.Tool('llvm')
570 env.Tool('udis86')
571
572 pkg_config_modules(env, 'x11', ['x11', 'xext'])
573 pkg_config_modules(env, 'drm', ['libdrm'])
574 pkg_config_modules(env, 'drm_intel', ['libdrm_intel'])
575 pkg_config_modules(env, 'drm_radeon', ['libdrm_radeon'])
576 pkg_config_modules(env, 'xorg', ['xorg-server'])
577 pkg_config_modules(env, 'kms', ['libkms'])
578
579 env['dri'] = env['x11'] and env['drm']
580
581 # Custom builders and methods
582 env.Tool('custom')
583 createInstallMethods(env)
584
585 # for debugging
586 #print env.Dump()
587
588
589 def exists(env):
590 return 1