0ed21a4e6ce13ed5e3db47ef01860ac3e349dd3e
[mesa.git] / scons / generic.py
1 """generic
2
3 Generic tool that provides a commmon ground for all platforms.
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 os
34 import os.path
35 import re
36 import platform as _platform
37 import sys
38
39 import SCons.Action
40 import SCons.Builder
41 import SCons.Scanner
42
43
44 def quietCommandLines(env):
45 # Quiet command lines
46 # See also http://www.scons.org/wiki/HidingCommandLinesInOutput
47 env['CCCOMSTR'] = "Compiling $SOURCE ..."
48 env['CXXCOMSTR'] = "Compiling $SOURCE ..."
49 env['ARCOMSTR'] = "Archiving $TARGET ..."
50 env['RANLIBCOMSTR'] = ""
51 env['LINKCOMSTR'] = "Linking $TARGET ..."
52
53
54 def createConvenienceLibBuilder(env):
55 """This is a utility function that creates the ConvenienceLibrary
56 Builder in an Environment if it is not there already.
57
58 If it is already there, we return the existing one.
59
60 Based on the stock StaticLibrary and SharedLibrary builders.
61 """
62
63 try:
64 convenience_lib = env['BUILDERS']['ConvenienceLibrary']
65 except KeyError:
66 action_list = [ SCons.Action.Action("$ARCOM", "$ARCOMSTR") ]
67 if env.Detect('ranlib'):
68 ranlib_action = SCons.Action.Action("$RANLIBCOM", "$RANLIBCOMSTR")
69 action_list.append(ranlib_action)
70
71 convenience_lib = SCons.Builder.Builder(action = action_list,
72 emitter = '$LIBEMITTER',
73 prefix = '$LIBPREFIX',
74 suffix = '$LIBSUFFIX',
75 src_suffix = '$SHOBJSUFFIX',
76 src_builder = 'SharedObject')
77 env['BUILDERS']['ConvenienceLibrary'] = convenience_lib
78
79 return convenience_lib
80
81
82 # TODO: handle import statements with multiple modules
83 # TODO: handle from import statements
84 import_re = re.compile(r'^import\s+(\S+)$', re.M)
85
86 def python_scan(node, env, path):
87 # http://www.scons.org/doc/0.98.5/HTML/scons-user/c2781.html#AEN2789
88 contents = node.get_contents()
89 source_dir = node.get_dir()
90 imports = import_re.findall(contents)
91 results = []
92 for imp in imports:
93 for dir in path:
94 file = os.path.join(str(dir), imp.replace('.', os.sep) + '.py')
95 if os.path.exists(file):
96 results.append(env.File(file))
97 break
98 file = os.path.join(str(dir), imp.replace('.', os.sep), '__init__.py')
99 if os.path.exists(file):
100 results.append(env.File(file))
101 break
102 return results
103
104 python_scanner = SCons.Scanner.Scanner(function = python_scan, skeys = ['.py'])
105
106
107 def code_generate(env, script, target, source, command):
108 """Method to simplify code generation via python scripts.
109
110 http://www.scons.org/wiki/UsingCodeGenerators
111 http://www.scons.org/doc/0.98.5/HTML/scons-user/c2768.html
112 """
113
114 # We're generating code using Python scripts, so we have to be
115 # careful with our scons elements. This entry represents
116 # the generator file *in the source directory*.
117 script_src = env.File(script).srcnode()
118
119 # This command creates generated code *in the build directory*.
120 command = command.replace('$SCRIPT', script_src.path)
121 code = env.Command(target, source, command)
122
123 # Explicitly mark that the generated code depends on the generator,
124 # and on implicitly imported python modules
125 path = (script_src.get_dir(),)
126 deps = [script_src]
127 deps += script_src.get_implicit_deps(env, python_scanner, path)
128 env.Depends(code, deps)
129
130 # Running the Python script causes .pyc files to be generated in the
131 # source directory. When we clean up, they should go too. So add side
132 # effects for .pyc files
133 for dep in deps:
134 pyc = env.File(str(dep) + 'c')
135 env.SideEffect(pyc, code)
136
137 return code
138
139
140 def createCodeGenerateMethod(env):
141 env.Append(SCANNERS = python_scanner)
142 env.AddMethod(code_generate, 'CodeGenerate')
143
144
145 def symlink(target, source, env):
146 target = str(target[0])
147 source = str(source[0])
148 if os.path.islink(target) or os.path.exists(target):
149 os.remove(target)
150 os.symlink(os.path.basename(source), target)
151
152 def install_shared_library(env, source, version = ()):
153 source = str(source[0])
154 version = tuple(map(str, version))
155 target_dir = os.path.join(env.Dir('#.').srcnode().abspath, env['build'], 'lib')
156 target_name = '.'.join((str(source),) + version)
157 last = env.InstallAs(os.path.join(target_dir, target_name), source)
158 while len(version):
159 version = version[:-1]
160 target_name = '.'.join((str(source),) + version)
161 action = SCons.Action.Action(symlink, "$TARGET -> $SOURCE")
162 last = env.Command(os.path.join(target_dir, target_name), last, action)
163
164 def createInstallMethods(env):
165 env.AddMethod(install_shared_library, 'InstallSharedLibrary')
166
167
168 _platform_map = {
169 'linux2': 'linux',
170 'win32': 'windows',
171 }
172
173
174 _machine_map = {
175 'x86': 'x86',
176 'i386': 'x86',
177 'i486': 'x86',
178 'i586': 'x86',
179 'i686': 'x86',
180 'ppc': 'ppc',
181 'x86_64': 'x86_64',
182 }
183
184
185 _toolchain_map = {
186 'winddk': 'winddk',
187 'wince': 'wcesdk',
188 }
189
190
191 _bool_map = {
192 'y': 1,
193 'yes': 1,
194 't': 1,
195 'true': 1,
196 '1': 1,
197 'on': 1,
198 'all': 1,
199 'n': 0,
200 'no': 0,
201 'f': 0,
202 'false': 0,
203 '0': 0,
204 'off': 0,
205 'none': 0,
206 }
207
208
209 def generate(env):
210 """Common environment generation code"""
211
212 from SCons.Script import ARGUMENTS
213
214 # FIXME: this is already too late
215 #if env.get('quiet', False):
216 # quietCommandLines(env)
217
218
219 # Platform
220 try:
221 env['platform'] = ARGUMENTS['platform']
222 except KeyError:
223 env['platform'] = _platform_map.get(sys.platform, sys.platform)
224
225 # Machine
226 try:
227 env['machine'] = ARGUMENTS['machine']
228 except KeyError:
229 env['machine'] = _machine_map.get(os.environ.get('PROCESSOR_ARCHITECTURE', _platform.machine()), 'generic')
230
231 # Toolchain
232 try:
233 env['toolchain'] = ARGUMENTS['toolchain']
234 except KeyError:
235 if env['platform'] in ('windows', 'winddk', 'wince') and sys.platform != 'win32':
236 env['toolchain'] = 'crossmingw'
237 else:
238 env['toolchain'] = _toolchain_map.get(env['platform'], 'default')
239 if env['toolchain'] == 'crossmingw' and env['machine'] not in ('generic', 'x86'):
240 env['machine'] = 'x86'
241
242 # Build type
243 env['debug'] = _bool_map[ARGUMENTS.get('debug', 'no')]
244 env['profile'] = _bool_map[ARGUMENTS.get('profile', 'no')]
245
246 # Put build output in a separate dir, which depends on the current
247 # configuration. See also http://www.scons.org/wiki/AdvancedBuildExample
248 try:
249 env['variant_dir'] = ARGUMENTS['variant_dir']
250 except KeyError:
251 build_topdir = 'build'
252 build_subdir = env['platform']
253 if env['machine'] != 'generic':
254 build_subdir += '-' + env['machine']
255 if env['debug']:
256 build_subdir += "-debug"
257 if env['profile']:
258 build_subdir += "-profile"
259 env['variant_dir'] = os.path.join(build_topdir, build_subdir)
260 # Place the .sconsign file in the build dir too, to avoid issues with
261 # different scons versions building the same source file
262 #env.VariantDir(env['variant_dir']
263 #env.SConsignFile(os.path.join(env['variant_dir'], '.sconsign'))
264
265 # Summary
266 print
267 print ' platform=%s' % env['platform']
268 print ' machine=%s' % env['machine']
269 print ' toolchain=%s' % env['toolchain']
270 print ' debug=%s' % ['no', 'yes'][env['debug']]
271 print ' profile=%s' % ['no', 'yes'][env['profile']]
272 #print ' variant_dir=%s' % env['variant_dir']
273 print
274
275 # Load tool chain
276 env.Tool(env['toolchain'])
277
278 # shortcuts
279 debug = env['debug']
280 machine = env['machine']
281 platform = env['platform']
282 x86 = env['machine'] == 'x86'
283 ppc = env['machine'] == 'ppc'
284 gcc = env['platform'] in ('linux', 'freebsd', 'darwin') or env['toolchain'] == 'crossmingw'
285 msvc = env['platform'] in ('windows', 'winddk', 'wince') and env['toolchain'] != 'crossmingw'
286
287 # C preprocessor options
288 cppdefines = []
289 if debug:
290 cppdefines += ['DEBUG']
291 else:
292 cppdefines += ['NDEBUG']
293 if env['profile']:
294 cppdefines += ['PROFILE']
295 if platform == 'windows':
296 cppdefines += [
297 'WIN32',
298 '_WINDOWS',
299 #'_UNICODE',
300 #'UNICODE',
301 # http://msdn2.microsoft.com/en-us/library/6dwk3a1z.aspx,
302 'WIN32_LEAN_AND_MEAN',
303 'VC_EXTRALEAN',
304 '_CRT_SECURE_NO_DEPRECATE',
305 ]
306 if debug:
307 cppdefines += ['_DEBUG']
308 if platform == 'winddk':
309 # Mimic WINDDK's builtin flags. See also:
310 # - WINDDK's bin/makefile.new i386mk.inc for more info.
311 # - buildchk_wxp_x86.log files, generated by the WINDDK's build
312 # - http://alter.org.ua/docs/nt_kernel/vc8_proj/
313 cppdefines += [
314 ('_X86_', '1'),
315 ('i386', '1'),
316 'STD_CALL',
317 ('CONDITION_HANDLING', '1'),
318 ('NT_INST', '0'),
319 ('WIN32', '100'),
320 ('_NT1X_', '100'),
321 ('WINNT', '1'),
322 ('_WIN32_WINNT', '0x0501'), # minimum required OS version
323 ('WINVER', '0x0501'),
324 ('_WIN32_IE', '0x0603'),
325 ('WIN32_LEAN_AND_MEAN', '1'),
326 ('DEVL', '1'),
327 ('__BUILDMACHINE__', 'WinDDK'),
328 ('FPO', '0'),
329 ]
330 if debug:
331 cppdefines += [('DBG', 1)]
332 if platform == 'wince':
333 cppdefines += [
334 '_CRT_SECURE_NO_DEPRECATE',
335 '_USE_32BIT_TIME_T',
336 'UNICODE',
337 '_UNICODE',
338 ('UNDER_CE', '600'),
339 ('_WIN32_WCE', '0x600'),
340 'WINCEOEM',
341 'WINCEINTERNAL',
342 'WIN32',
343 'STRICT',
344 'x86',
345 '_X86_',
346 'INTERNATIONAL',
347 ('INTLMSG_CODEPAGE', '1252'),
348 ]
349 env.Append(CPPDEFINES = cppdefines)
350
351 # C preprocessor includes
352 if platform == 'winddk':
353 env.Append(CPPPATH = [
354 env['SDK_INC_PATH'],
355 env['DDK_INC_PATH'],
356 env['WDM_INC_PATH'],
357 env['CRT_INC_PATH'],
358 ])
359
360 # C compiler options
361 cflags = []
362 if gcc:
363 if debug:
364 cflags += ['-O0', '-g3']
365 else:
366 cflags += ['-O3', '-g0']
367 if env['profile']:
368 cflags += ['-pg']
369 if env['machine'] == 'x86':
370 cflags += [
371 '-m32',
372 #'-march=pentium4',
373 '-mmmx', '-msse', '-msse2', # enable SIMD intrinsics
374 #'-mfpmath=sse',
375 ]
376 if env['machine'] == 'x86_64':
377 cflags += ['-m64']
378 cflags += [
379 '-Wall',
380 '-Wmissing-prototypes',
381 '-Wno-long-long',
382 '-ffast-math',
383 '-pedantic',
384 '-fmessage-length=0', # be nice to Eclipse
385 ]
386 if msvc:
387 # See also:
388 # - http://msdn.microsoft.com/en-us/library/19z1t1wy.aspx
389 # - cl /?
390 if debug:
391 cflags += [
392 '/Od', # disable optimizations
393 '/Oi', # enable intrinsic functions
394 '/Oy-', # disable frame pointer omission
395 ]
396 else:
397 cflags += [
398 '/Ox', # maximum optimizations
399 '/Oi', # enable intrinsic functions
400 '/Ot', # favor code speed
401 #'/fp:fast', # fast floating point
402 ]
403 if env['profile']:
404 cflags += [
405 '/Gh', # enable _penter hook function
406 '/GH', # enable _pexit hook function
407 ]
408 cflags += [
409 '/W3', # warning level
410 #'/Wp64', # enable 64 bit porting warnings
411 ]
412 if env['machine'] == 'x86':
413 cflags += [
414 #'/QIfist', # Suppress _ftol
415 #'/arch:SSE2', # use the SSE2 instructions
416 ]
417 if platform == 'windows':
418 cflags += [
419 # TODO
420 ]
421 if platform == 'winddk':
422 cflags += [
423 '/Zl', # omit default library name in .OBJ
424 '/Zp8', # 8bytes struct member alignment
425 '/Gy', # separate functions for linker
426 '/Gm-', # disable minimal rebuild
427 '/WX', # treat warnings as errors
428 '/Gz', # __stdcall Calling convention
429 '/GX-', # disable C++ EH
430 '/GR-', # disable C++ RTTI
431 '/GF', # enable read-only string pooling
432 '/G6', # optimize for PPro, P-II, P-III
433 '/Ze', # enable extensions
434 '/Gi-', # disable incremental compilation
435 '/QIfdiv-', # disable Pentium FDIV fix
436 '/hotpatch', # prepares an image for hotpatching.
437 #'/Z7', #enable old-style debug info
438 ]
439 if platform == 'wince':
440 # See also C:\WINCE600\public\common\oak\misc\makefile.def
441 cflags += [
442 '/Zl', # omit default library name in .OBJ
443 '/GF', # enable read-only string pooling
444 '/GR-', # disable C++ RTTI
445 '/GS', # enable security checks
446 # Allow disabling language conformance to maintain backward compat
447 #'/Zc:wchar_t-', # don't force wchar_t as native type, instead of typedef
448 #'/Zc:forScope-', # don't enforce Standard C++ for scoping rules
449 #'/wd4867',
450 #'/wd4430',
451 #'/MT',
452 #'/U_MT',
453 ]
454 # Automatic pdb generation
455 # See http://scons.tigris.org/issues/show_bug.cgi?id=1656
456 env.EnsureSConsVersion(0, 98, 0)
457 env['PDB'] = '${TARGET.base}.pdb'
458 env.Append(CFLAGS = cflags)
459 env.Append(CXXFLAGS = cflags)
460
461 if env['platform'] == 'windows' and msvc:
462 # Choose the appropriate MSVC CRT
463 # http://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx
464 if env['debug']:
465 env.Append(CCFLAGS = ['/MTd'])
466 env.Append(SHCCFLAGS = ['/LDd'])
467 else:
468 env.Append(CCFLAGS = ['/MT'])
469 env.Append(SHCCFLAGS = ['/LD'])
470
471 # Assembler options
472 if gcc:
473 if env['machine'] == 'x86':
474 env.Append(ASFLAGS = ['-m32'])
475 if env['machine'] == 'x86_64':
476 env.Append(ASFLAGS = ['-m64'])
477
478 # Linker options
479 linkflags = []
480 if gcc:
481 if env['machine'] == 'x86':
482 linkflags += ['-m32']
483 if env['machine'] == 'x86_64':
484 linkflags += ['-m64']
485 if platform == 'winddk':
486 # See also:
487 # - http://msdn2.microsoft.com/en-us/library/y0zzbyt4.aspx
488 linkflags += [
489 '/merge:_PAGE=PAGE',
490 '/merge:_TEXT=.text',
491 '/section:INIT,d',
492 '/opt:ref',
493 '/opt:icf',
494 '/ignore:4198,4010,4037,4039,4065,4070,4078,4087,4089,4221',
495 '/incremental:no',
496 '/fullbuild',
497 '/release',
498 '/nodefaultlib',
499 '/wx',
500 '/debug',
501 '/debugtype:cv',
502 '/version:5.1',
503 '/osversion:5.1',
504 '/functionpadmin:5',
505 '/safeseh',
506 '/pdbcompress',
507 '/stack:0x40000,0x1000',
508 '/driver',
509 '/align:0x80',
510 '/subsystem:native,5.01',
511 '/base:0x10000',
512
513 '/entry:DrvEnableDriver',
514 ]
515 if env['profile']:
516 linkflags += [
517 '/MAP', # http://msdn.microsoft.com/en-us/library/k7xkk3e2.aspx
518 ]
519 if platform == 'wince':
520 linkflags += [
521 '/nodefaultlib',
522 #'/incremental:no',
523 #'/fullbuild',
524 '/entry:_DllMainCRTStartup',
525 ]
526 env.Append(LINKFLAGS = linkflags)
527
528 # Default libs
529 env.Append(LIBS = [])
530
531 # Custom builders and methods
532 createConvenienceLibBuilder(env)
533 createCodeGenerateMethod(env)
534 createInstallMethods(env)
535
536 # for debugging
537 #print env.Dump()
538
539
540 def exists(env):
541 return 1