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