blob: df7ef42402ee8ec7a73ce20088f41f411f8c032d [file] [log] [blame]
José Fonseca72ebf4f2008-11-21 03:40:48 +09001"""generic
2
3Generic 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
33import os
34import os.path
35import re
36import platform as _platform
37import sys
38
39import SCons.Action
40import SCons.Builder
41import SCons.Scanner
42
43
44def 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
54def 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
84import_re = re.compile(r'^import\s+(\S+)$', re.M)
85
86def 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
104python_scanner = SCons.Scanner.Scanner(function = python_scan, skeys = ['.py'])
105
106
107def 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
140def createCodeGenerateMethod(env):
141 env.Append(SCANNERS = python_scanner)
142 env.AddMethod(code_generate, 'CodeGenerate')
143
144
145def 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
152def 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
164def 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
209def 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
José Fonsecade29f572009-01-23 16:25:37 +0000242 try:
243 env['MSVS_VERSION'] = ARGUMENTS['MSVS_VERSION']
244 except KeyError:
245 pass
246
José Fonseca72ebf4f2008-11-21 03:40:48 +0900247 # Build type
248 env['debug'] = _bool_map[ARGUMENTS.get('debug', 'no')]
249 env['profile'] = _bool_map[ARGUMENTS.get('profile', 'no')]
250
251 # Put build output in a separate dir, which depends on the current
252 # configuration. See also http://www.scons.org/wiki/AdvancedBuildExample
253 try:
José Fonseca9bf83fb2009-01-24 15:56:28 +0000254 env['build'] = ARGUMENTS['build']
José Fonseca72ebf4f2008-11-21 03:40:48 +0900255 except KeyError:
256 build_topdir = 'build'
257 build_subdir = env['platform']
258 if env['machine'] != 'generic':
259 build_subdir += '-' + env['machine']
260 if env['debug']:
261 build_subdir += "-debug"
262 if env['profile']:
263 build_subdir += "-profile"
José Fonseca9bf83fb2009-01-24 15:56:28 +0000264 env['build'] = os.path.join(build_topdir, build_subdir)
José Fonseca72ebf4f2008-11-21 03:40:48 +0900265 # Place the .sconsign file in the build dir too, to avoid issues with
266 # different scons versions building the same source file
José Fonseca9bf83fb2009-01-24 15:56:28 +0000267 env.SConsignFile(os.path.join(env['build'], '.sconsign'))
José Fonseca72ebf4f2008-11-21 03:40:48 +0900268
269 # Summary
270 print
271 print ' platform=%s' % env['platform']
272 print ' machine=%s' % env['machine']
273 print ' toolchain=%s' % env['toolchain']
274 print ' debug=%s' % ['no', 'yes'][env['debug']]
275 print ' profile=%s' % ['no', 'yes'][env['profile']]
José Fonseca9bf83fb2009-01-24 15:56:28 +0000276 print ' build=%s' % env['build']
José Fonseca72ebf4f2008-11-21 03:40:48 +0900277 print
278
279 # Load tool chain
280 env.Tool(env['toolchain'])
281
282 # shortcuts
283 debug = env['debug']
284 machine = env['machine']
285 platform = env['platform']
286 x86 = env['machine'] == 'x86'
287 ppc = env['machine'] == 'ppc'
288 gcc = env['platform'] in ('linux', 'freebsd', 'darwin') or env['toolchain'] == 'crossmingw'
289 msvc = env['platform'] in ('windows', 'winddk', 'wince') and env['toolchain'] != 'crossmingw'
290
291 # C preprocessor options
292 cppdefines = []
293 if debug:
294 cppdefines += ['DEBUG']
295 else:
296 cppdefines += ['NDEBUG']
297 if env['profile']:
298 cppdefines += ['PROFILE']
299 if platform == 'windows':
300 cppdefines += [
301 'WIN32',
302 '_WINDOWS',
303 #'_UNICODE',
304 #'UNICODE',
305 # http://msdn2.microsoft.com/en-us/library/6dwk3a1z.aspx,
306 'WIN32_LEAN_AND_MEAN',
307 'VC_EXTRALEAN',
308 '_CRT_SECURE_NO_DEPRECATE',
309 ]
310 if debug:
311 cppdefines += ['_DEBUG']
312 if platform == 'winddk':
313 # Mimic WINDDK's builtin flags. See also:
314 # - WINDDK's bin/makefile.new i386mk.inc for more info.
315 # - buildchk_wxp_x86.log files, generated by the WINDDK's build
316 # - http://alter.org.ua/docs/nt_kernel/vc8_proj/
317 cppdefines += [
318 ('_X86_', '1'),
319 ('i386', '1'),
320 'STD_CALL',
321 ('CONDITION_HANDLING', '1'),
322 ('NT_INST', '0'),
323 ('WIN32', '100'),
324 ('_NT1X_', '100'),
325 ('WINNT', '1'),
326 ('_WIN32_WINNT', '0x0501'), # minimum required OS version
327 ('WINVER', '0x0501'),
328 ('_WIN32_IE', '0x0603'),
329 ('WIN32_LEAN_AND_MEAN', '1'),
330 ('DEVL', '1'),
331 ('__BUILDMACHINE__', 'WinDDK'),
332 ('FPO', '0'),
333 ]
334 if debug:
335 cppdefines += [('DBG', 1)]
336 if platform == 'wince':
337 cppdefines += [
338 '_CRT_SECURE_NO_DEPRECATE',
339 '_USE_32BIT_TIME_T',
340 'UNICODE',
341 '_UNICODE',
342 ('UNDER_CE', '600'),
343 ('_WIN32_WCE', '0x600'),
344 'WINCEOEM',
345 'WINCEINTERNAL',
346 'WIN32',
347 'STRICT',
348 'x86',
349 '_X86_',
350 'INTERNATIONAL',
351 ('INTLMSG_CODEPAGE', '1252'),
352 ]
353 env.Append(CPPDEFINES = cppdefines)
354
355 # C preprocessor includes
356 if platform == 'winddk':
357 env.Append(CPPPATH = [
358 env['SDK_INC_PATH'],
359 env['DDK_INC_PATH'],
360 env['WDM_INC_PATH'],
361 env['CRT_INC_PATH'],
362 ])
363
364 # C compiler options
365 cflags = []
366 if gcc:
367 if debug:
368 cflags += ['-O0', '-g3']
369 else:
370 cflags += ['-O3', '-g0']
371 if env['profile']:
372 cflags += ['-pg']
373 if env['machine'] == 'x86':
374 cflags += [
375 '-m32',
376 #'-march=pentium4',
377 '-mmmx', '-msse', '-msse2', # enable SIMD intrinsics
378 #'-mfpmath=sse',
379 ]
380 if env['machine'] == 'x86_64':
381 cflags += ['-m64']
382 cflags += [
383 '-Wall',
384 '-Wmissing-prototypes',
385 '-Wno-long-long',
386 '-ffast-math',
387 '-pedantic',
388 '-fmessage-length=0', # be nice to Eclipse
389 ]
390 if msvc:
391 # See also:
392 # - http://msdn.microsoft.com/en-us/library/19z1t1wy.aspx
393 # - cl /?
394 if debug:
395 cflags += [
396 '/Od', # disable optimizations
397 '/Oi', # enable intrinsic functions
398 '/Oy-', # disable frame pointer omission
399 ]
400 else:
401 cflags += [
402 '/Ox', # maximum optimizations
403 '/Oi', # enable intrinsic functions
404 '/Ot', # favor code speed
405 #'/fp:fast', # fast floating point
406 ]
407 if env['profile']:
408 cflags += [
409 '/Gh', # enable _penter hook function
410 '/GH', # enable _pexit hook function
411 ]
412 cflags += [
413 '/W3', # warning level
414 #'/Wp64', # enable 64 bit porting warnings
415 ]
416 if env['machine'] == 'x86':
417 cflags += [
418 #'/QIfist', # Suppress _ftol
419 #'/arch:SSE2', # use the SSE2 instructions
420 ]
421 if platform == 'windows':
422 cflags += [
423 # TODO
424 ]
425 if platform == 'winddk':
426 cflags += [
427 '/Zl', # omit default library name in .OBJ
428 '/Zp8', # 8bytes struct member alignment
429 '/Gy', # separate functions for linker
430 '/Gm-', # disable minimal rebuild
431 '/WX', # treat warnings as errors
432 '/Gz', # __stdcall Calling convention
433 '/GX-', # disable C++ EH
434 '/GR-', # disable C++ RTTI
435 '/GF', # enable read-only string pooling
436 '/G6', # optimize for PPro, P-II, P-III
437 '/Ze', # enable extensions
438 '/Gi-', # disable incremental compilation
439 '/QIfdiv-', # disable Pentium FDIV fix
440 '/hotpatch', # prepares an image for hotpatching.
441 #'/Z7', #enable old-style debug info
442 ]
443 if platform == 'wince':
444 # See also C:\WINCE600\public\common\oak\misc\makefile.def
445 cflags += [
446 '/Zl', # omit default library name in .OBJ
447 '/GF', # enable read-only string pooling
448 '/GR-', # disable C++ RTTI
449 '/GS', # enable security checks
450 # Allow disabling language conformance to maintain backward compat
451 #'/Zc:wchar_t-', # don't force wchar_t as native type, instead of typedef
452 #'/Zc:forScope-', # don't enforce Standard C++ for scoping rules
453 #'/wd4867',
454 #'/wd4430',
455 #'/MT',
456 #'/U_MT',
457 ]
458 # Automatic pdb generation
459 # See http://scons.tigris.org/issues/show_bug.cgi?id=1656
460 env.EnsureSConsVersion(0, 98, 0)
461 env['PDB'] = '${TARGET.base}.pdb'
462 env.Append(CFLAGS = cflags)
463 env.Append(CXXFLAGS = cflags)
464
José Fonseca1781d7f2009-01-06 16:16:38 +0000465 if env['platform'] == 'windows' and msvc:
466 # Choose the appropriate MSVC CRT
467 # http://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx
468 if env['debug']:
469 env.Append(CCFLAGS = ['/MTd'])
470 env.Append(SHCCFLAGS = ['/LDd'])
471 else:
472 env.Append(CCFLAGS = ['/MT'])
473 env.Append(SHCCFLAGS = ['/LD'])
474
José Fonseca72ebf4f2008-11-21 03:40:48 +0900475 # Assembler options
476 if gcc:
477 if env['machine'] == 'x86':
478 env.Append(ASFLAGS = ['-m32'])
479 if env['machine'] == 'x86_64':
480 env.Append(ASFLAGS = ['-m64'])
481
482 # Linker options
483 linkflags = []
484 if gcc:
485 if env['machine'] == 'x86':
486 linkflags += ['-m32']
487 if env['machine'] == 'x86_64':
488 linkflags += ['-m64']
489 if platform == 'winddk':
490 # See also:
491 # - http://msdn2.microsoft.com/en-us/library/y0zzbyt4.aspx
492 linkflags += [
493 '/merge:_PAGE=PAGE',
494 '/merge:_TEXT=.text',
495 '/section:INIT,d',
496 '/opt:ref',
497 '/opt:icf',
498 '/ignore:4198,4010,4037,4039,4065,4070,4078,4087,4089,4221',
499 '/incremental:no',
500 '/fullbuild',
501 '/release',
502 '/nodefaultlib',
503 '/wx',
504 '/debug',
505 '/debugtype:cv',
506 '/version:5.1',
507 '/osversion:5.1',
508 '/functionpadmin:5',
509 '/safeseh',
510 '/pdbcompress',
511 '/stack:0x40000,0x1000',
512 '/driver',
513 '/align:0x80',
514 '/subsystem:native,5.01',
515 '/base:0x10000',
516
517 '/entry:DrvEnableDriver',
518 ]
519 if env['profile']:
520 linkflags += [
521 '/MAP', # http://msdn.microsoft.com/en-us/library/k7xkk3e2.aspx
522 ]
523 if platform == 'wince':
524 linkflags += [
525 '/nodefaultlib',
526 #'/incremental:no',
527 #'/fullbuild',
528 '/entry:_DllMainCRTStartup',
529 ]
530 env.Append(LINKFLAGS = linkflags)
531
532 # Default libs
533 env.Append(LIBS = [])
534
535 # Custom builders and methods
536 createConvenienceLibBuilder(env)
537 createCodeGenerateMethod(env)
538 createInstallMethods(env)
539
540 # for debugging
541 #print env.Dump()
542
543
544def exists(env):
545 return 1