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