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