gallium.py revision 8b7552673f38b47abb432985e205785accdae5df
1"""gallium
2
3Frontend-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
33import os
34import os.path
35import re
36
37import SCons.Action
38import SCons.Builder
39import SCons.Scanner
40
41import fixes
42
43
44def quietCommandLines(env):
45    # Quiet command lines
46    # See also http://www.scons.org/wiki/HidingCommandLinesInOutput
47    env['ASCOMSTR'] = "  Assembling $SOURCE ..."
48    env['ASPPCOMSTR'] = "  Assembling $SOURCE ..."
49    env['CCCOMSTR'] = "  Compiling $SOURCE ..."
50    env['SHCCCOMSTR'] = "  Compiling $SOURCE ..."
51    env['CXXCOMSTR'] = "  Compiling $SOURCE ..."
52    env['SHCXXCOMSTR'] = "  Compiling $SOURCE ..."
53    env['ARCOMSTR'] = "  Archiving $TARGET ..."
54    env['RANLIBCOMSTR'] = "  Indexing $TARGET ..."
55    env['LINKCOMSTR'] = "  Linking $TARGET ..."
56    env['SHLINKCOMSTR'] = "  Linking $TARGET ..."
57    env['LDMODULECOMSTR'] = "  Linking $TARGET ..."
58    env['SWIGCOMSTR'] = "  Generating $TARGET ..."
59
60
61def createConvenienceLibBuilder(env):
62    """This is a utility function that creates the ConvenienceLibrary
63    Builder in an Environment if it is not there already.
64
65    If it is already there, we return the existing one.
66
67    Based on the stock StaticLibrary and SharedLibrary builders.
68    """
69
70    try:
71        convenience_lib = env['BUILDERS']['ConvenienceLibrary']
72    except KeyError:
73        action_list = [ SCons.Action.Action("$ARCOM", "$ARCOMSTR") ]
74        if env.Detect('ranlib'):
75            ranlib_action = SCons.Action.Action("$RANLIBCOM", "$RANLIBCOMSTR")
76            action_list.append(ranlib_action)
77
78        convenience_lib = SCons.Builder.Builder(action = action_list,
79                                  emitter = '$LIBEMITTER',
80                                  prefix = '$LIBPREFIX',
81                                  suffix = '$LIBSUFFIX',
82                                  src_suffix = '$SHOBJSUFFIX',
83                                  src_builder = 'SharedObject')
84        env['BUILDERS']['ConvenienceLibrary'] = convenience_lib
85
86    return convenience_lib
87
88
89# TODO: handle import statements with multiple modules
90# TODO: handle from import statements
91import_re = re.compile(r'^import\s+(\S+)$', re.M)
92
93def python_scan(node, env, path):
94    # http://www.scons.org/doc/0.98.5/HTML/scons-user/c2781.html#AEN2789
95    contents = node.get_contents()
96    source_dir = node.get_dir()
97    imports = import_re.findall(contents)
98    results = []
99    for imp in imports:
100        for dir in path:
101            file = os.path.join(str(dir), imp.replace('.', os.sep) + '.py')
102            if os.path.exists(file):
103                results.append(env.File(file))
104                break
105            file = os.path.join(str(dir), imp.replace('.', os.sep), '__init__.py')
106            if os.path.exists(file):
107                results.append(env.File(file))
108                break
109    return results
110
111python_scanner = SCons.Scanner.Scanner(function = python_scan, skeys = ['.py'])
112
113
114def code_generate(env, script, target, source, command):
115    """Method to simplify code generation via python scripts.
116
117    http://www.scons.org/wiki/UsingCodeGenerators
118    http://www.scons.org/doc/0.98.5/HTML/scons-user/c2768.html
119    """
120
121    # We're generating code using Python scripts, so we have to be
122    # careful with our scons elements.  This entry represents
123    # the generator file *in the source directory*.
124    script_src = env.File(script).srcnode()
125
126    # This command creates generated code *in the build directory*.
127    command = command.replace('$SCRIPT', script_src.path)
128    code = env.Command(target, source, command)
129
130    # Explicitly mark that the generated code depends on the generator,
131    # and on implicitly imported python modules
132    path = (script_src.get_dir(),)
133    deps = [script_src]
134    deps += script_src.get_implicit_deps(env, python_scanner, path)
135    env.Depends(code, deps)
136
137    # Running the Python script causes .pyc files to be generated in the
138    # source directory.  When we clean up, they should go too. So add side
139    # effects for .pyc files
140    for dep in deps:
141        pyc = env.File(str(dep) + 'c')
142        env.SideEffect(pyc, code)
143
144    return code
145
146
147def createCodeGenerateMethod(env):
148    env.Append(SCANNERS = python_scanner)
149    env.AddMethod(code_generate, 'CodeGenerate')
150
151
152def symlink(target, source, env):
153    target = str(target[0])
154    source = str(source[0])
155    if os.path.islink(target) or os.path.exists(target):
156        os.remove(target)
157    os.symlink(os.path.basename(source), target)
158
159def install_program(env, source):
160    source = str(source[0])
161    target_dir =  os.path.join(env.Dir('#.').srcnode().abspath, env['build'], 'bin')
162    target_name = str(source)
163    env.InstallAs(os.path.join(target_dir, target_name), source)
164
165def install_shared_library(env, source, version = ()):
166    source = str(source[0])
167    version = tuple(map(str, version))
168    target_dir =  os.path.join(env.Dir('#.').srcnode().abspath, env['build'], 'lib')
169    target_name = '.'.join((str(source),) + version)
170    last = env.InstallAs(os.path.join(target_dir, target_name), source)
171    while len(version):
172        version = version[:-1]
173        target_name = '.'.join((str(source),) + version)
174        action = SCons.Action.Action(symlink, "$TARGET -> $SOURCE")
175        last = env.Command(os.path.join(target_dir, target_name), last, action)
176
177def createInstallMethods(env):
178    env.AddMethod(install_program, 'InstallProgram')
179    env.AddMethod(install_shared_library, 'InstallSharedLibrary')
180
181
182def num_jobs():
183    try:
184        return int(os.environ['NUMBER_OF_PROCESSORS'])
185    except (ValueError, KeyError):
186        pass
187
188    try:
189        return os.sysconf('SC_NPROCESSORS_ONLN')
190    except (ValueError, OSError, AttributeError):
191        pass
192
193    try:
194        return int(os.popen2("sysctl -n hw.ncpu")[1].read())
195    except ValueError:
196        pass
197
198    return 1
199
200
201def generate(env):
202    """Common environment generation code"""
203
204    if env.get('quiet', True):
205        quietCommandLines(env)
206
207    # Toolchain
208    platform = env['platform']
209    if env['toolchain'] == 'default':
210        if platform == 'winddk':
211            env['toolchain'] = 'winddk'
212        elif platform == 'wince':
213            env['toolchain'] = 'wcesdk'
214    env.Tool(env['toolchain'])
215
216    env['gcc'] = 'gcc' in os.path.basename(env['CC']).split('-')
217    env['msvc'] = env['CC'] == 'cl'
218
219    # shortcuts
220    debug = env['debug']
221    machine = env['machine']
222    platform = env['platform']
223    x86 = env['machine'] == 'x86'
224    ppc = env['machine'] == 'ppc'
225    gcc = env['gcc']
226    msvc = env['msvc']
227
228    # Put build output in a separate dir, which depends on the current
229    # configuration. See also http://www.scons.org/wiki/AdvancedBuildExample
230    build_topdir = 'build'
231    build_subdir = env['platform']
232    if env['llvm']:
233        build_subdir += "-llvm"
234    if env['machine'] != 'generic':
235        build_subdir += '-' + env['machine']
236    if env['debug']:
237        build_subdir += "-debug"
238    if env['profile']:
239        build_subdir += "-profile"
240    build_dir = os.path.join(build_topdir, build_subdir)
241    # Place the .sconsign file in the build dir too, to avoid issues with
242    # different scons versions building the same source file
243    env['build'] = build_dir
244    env.SConsignFile(os.path.join(build_dir, '.sconsign'))
245    env.CacheDir('build/cache')
246    env['CONFIGUREDIR'] = os.path.join(build_dir, 'conf')
247    env['CONFIGURELOG'] = os.path.join(os.path.abspath(build_dir), 'config.log')
248
249    # Parallel build
250    if env.GetOption('num_jobs') <= 1:
251        env.SetOption('num_jobs', num_jobs())
252
253    # C preprocessor options
254    cppdefines = []
255    if debug:
256        cppdefines += ['DEBUG']
257    else:
258        cppdefines += ['NDEBUG']
259    if env['profile']:
260        cppdefines += ['PROFILE']
261    if platform == 'windows':
262        cppdefines += [
263            'WIN32',
264            '_WINDOWS',
265            #'_UNICODE',
266            #'UNICODE',
267            ('_WIN32_WINNT', '0x0501'), # minimum required OS version
268            ('WINVER', '0x0501'),
269            # http://msdn2.microsoft.com/en-us/library/6dwk3a1z.aspx,
270            'WIN32_LEAN_AND_MEAN',
271        ]
272        if msvc and env['toolchain'] != 'winddk':
273            cppdefines += [
274                'VC_EXTRALEAN',
275                '_USE_MATH_DEFINES',
276                '_CRT_SECURE_NO_WARNINGS',
277                '_CRT_SECURE_NO_DEPRECATE',
278                '_SCL_SECURE_NO_WARNINGS',
279                '_SCL_SECURE_NO_DEPRECATE',
280            ]
281        if debug:
282            cppdefines += ['_DEBUG']
283    if env['toolchain'] == 'winddk':
284        # Mimic WINDDK's builtin flags. See also:
285        # - WINDDK's bin/makefile.new i386mk.inc for more info.
286        # - buildchk_wxp_x86.log files, generated by the WINDDK's build
287        # - http://alter.org.ua/docs/nt_kernel/vc8_proj/
288        if machine == 'x86':
289            cppdefines += ['_X86_', 'i386']
290        if machine == 'x86_64':
291            cppdefines += ['_AMD64_', 'AMD64']
292    if platform == 'winddk':
293        cppdefines += [
294            'STD_CALL',
295            ('CONDITION_HANDLING', '1'),
296            ('NT_INST', '0'),
297            ('WIN32', '100'),
298            ('_NT1X_', '100'),
299            ('WINNT', '1'),
300            ('_WIN32_WINNT', '0x0501'), # minimum required OS version
301            ('WINVER', '0x0501'),
302            ('_WIN32_IE', '0x0603'),
303            ('WIN32_LEAN_AND_MEAN', '1'),
304            ('DEVL', '1'),
305            ('__BUILDMACHINE__', 'WinDDK'),
306            ('FPO', '0'),
307        ]
308        if debug:
309            cppdefines += [('DBG', 1)]
310    if platform == 'wince':
311        cppdefines += [
312            '_CRT_SECURE_NO_DEPRECATE',
313            '_USE_32BIT_TIME_T',
314            'UNICODE',
315            '_UNICODE',
316            ('UNDER_CE', '600'),
317            ('_WIN32_WCE', '0x600'),
318            'WINCEOEM',
319            'WINCEINTERNAL',
320            'WIN32',
321            'STRICT',
322            'x86',
323            '_X86_',
324            'INTERNATIONAL',
325            ('INTLMSG_CODEPAGE', '1252'),
326        ]
327    if platform == 'windows':
328        cppdefines += ['PIPE_SUBSYSTEM_WINDOWS_USER']
329    if platform == 'winddk':
330        cppdefines += ['PIPE_SUBSYSTEM_WINDOWS_DISPLAY']
331    if platform == 'wince':
332        cppdefines += ['PIPE_SUBSYSTEM_WINDOWS_CE']
333        cppdefines += ['PIPE_SUBSYSTEM_WINDOWS_CE_OGL']
334    env.Append(CPPDEFINES = cppdefines)
335
336    # C compiler options
337    cflags = [] # C
338    cxxflags = [] # C++
339    ccflags = [] # C & C++
340    if gcc:
341        if debug:
342            ccflags += ['-O0', '-g3']
343        elif env['CCVERSION'].startswith('4.2.'):
344            # gcc 4.2.x optimizer is broken
345            print "warning: gcc 4.2.x optimizer is broken -- disabling optimizations"
346            ccflags += ['-O0', '-g3']
347        else:
348            ccflags += ['-O3', '-g3']
349        if env['profile']:
350            # See http://code.google.com/p/jrfonseca/wiki/Gprof2Dot#Which_options_should_I_pass_to_gcc_when_compiling_for_profiling?
351            ccflags += [
352                '-fno-omit-frame-pointer',
353                '-fno-optimize-sibling-calls',
354            ]
355        if env['machine'] == 'x86':
356            ccflags += [
357                '-m32',
358                #'-march=pentium4',
359                #'-mfpmath=sse',
360            ]
361            if platform != 'windows':
362                # XXX: -mstackrealign causes stack corruption on MinGW. Ditto
363                # for -mincoming-stack-boundary=2.  Still enable it on other
364                # platforms for now, but we can't rely on it for cross platform
365                # code. We have to use __attribute__((force_align_arg_pointer))
366                # instead.
367                ccflags += [
368                    '-mmmx', '-msse', '-msse2', # enable SIMD intrinsics
369                    '-mstackrealign', # ensure stack is aligned
370                ]
371        if env['machine'] == 'x86_64':
372            ccflags += ['-m64']
373        # See also:
374        # - http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
375        ccflags += [
376            '-Wall',
377            '-Wmissing-field-initializers',
378            '-Werror=pointer-arith',
379            '-Wno-long-long',
380            '-ffast-math',
381            '-fmessage-length=0', # be nice to Eclipse
382        ]
383        cflags += [
384            '-Werror=declaration-after-statement',
385            '-Wmissing-prototypes',
386            '-std=gnu99',
387        ]
388    if msvc:
389        # See also:
390        # - http://msdn.microsoft.com/en-us/library/19z1t1wy.aspx
391        # - cl /?
392        if 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['debug']:
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        shlinkflags += [
484            '-Wl,-Bsymbolic',
485        ]
486        # Handle circular dependencies in the libraries
487        env['_LIBFLAGS'] = '-Wl,--start-group ' + env['_LIBFLAGS'] + ' -Wl,--end-group'
488    if msvc:
489        if not env['debug']:
490            # enable Link-time Code Generation
491            linkflags += ['/LTCG']
492            env.Append(ARFLAGS = ['/LTCG'])
493    if platform == 'windows' and msvc:
494        # See also:
495        # - http://msdn2.microsoft.com/en-us/library/y0zzbyt4.aspx
496        linkflags += [
497            '/fixed:no',
498            '/incremental:no',
499        ]
500    if platform == 'winddk':
501        linkflags += [
502            '/merge:_PAGE=PAGE',
503            '/merge:_TEXT=.text',
504            '/section:INIT,d',
505            '/opt:ref',
506            '/opt:icf',
507            '/ignore:4198,4010,4037,4039,4065,4070,4078,4087,4089,4221',
508            '/incremental:no',
509            '/fullbuild',
510            '/release',
511            '/nodefaultlib',
512            '/wx',
513            '/debug',
514            '/debugtype:cv',
515            '/version:5.1',
516            '/osversion:5.1',
517            '/functionpadmin:5',
518            '/safeseh',
519            '/pdbcompress',
520            '/stack:0x40000,0x1000',
521            '/driver',
522            '/align:0x80',
523            '/subsystem:native,5.01',
524            '/base:0x10000',
525
526            '/entry:DrvEnableDriver',
527        ]
528        if env['debug'] or env['profile']:
529            linkflags += [
530                '/MAP', # http://msdn.microsoft.com/en-us/library/k7xkk3e2.aspx
531            ]
532    if platform == 'wince':
533        linkflags += [
534            '/nodefaultlib',
535            #'/incremental:no',
536            #'/fullbuild',
537            '/entry:_DllMainCRTStartup',
538        ]
539    env.Append(LINKFLAGS = linkflags)
540    env.Append(SHLINKFLAGS = shlinkflags)
541
542    # Default libs
543    env.Append(LIBS = [])
544
545    # Custom builders and methods
546    createConvenienceLibBuilder(env)
547    createCodeGenerateMethod(env)
548    createInstallMethods(env)
549
550    # for debugging
551    #print env.Dump()
552
553
554def exists(env):
555    return 1
556