gallium.py revision 848b8605
1"""gallium
2
3Frontend-tool for Gallium3D architecture.
4
5"""
6
7#
8# Copyright 2008 VMware, Inc.
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 VMWARE 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 distutils.version
34import os
35import os.path
36import re
37import subprocess
38import platform as _platform
39import sys
40import tempfile
41
42import SCons.Action
43import SCons.Builder
44import SCons.Scanner
45
46
47def symlink(target, source, env):
48    target = str(target[0])
49    source = str(source[0])
50    if os.path.islink(target) or os.path.exists(target):
51        os.remove(target)
52    os.symlink(os.path.basename(source), target)
53
54def install(env, source, subdir):
55    target_dir = os.path.join(env.Dir('#.').srcnode().abspath, env['build_dir'], subdir)
56    return env.Install(target_dir, source)
57
58def install_program(env, source):
59    return install(env, source, 'bin')
60
61def install_shared_library(env, sources, version = ()):
62    targets = []
63    install_dir = os.path.join(env.Dir('#.').srcnode().abspath, env['build_dir'])
64    version = tuple(map(str, version))
65    if env['SHLIBSUFFIX'] == '.dll':
66        dlls = env.FindIxes(sources, 'SHLIBPREFIX', 'SHLIBSUFFIX')
67        targets += install(env, dlls, 'bin')
68        libs = env.FindIxes(sources, 'LIBPREFIX', 'LIBSUFFIX')
69        targets += install(env, libs, 'lib')
70    else:
71        for source in sources:
72            target_dir =  os.path.join(install_dir, 'lib')
73            target_name = '.'.join((str(source),) + version)
74            last = env.InstallAs(os.path.join(target_dir, target_name), source)
75            targets += last
76            while len(version):
77                version = version[:-1]
78                target_name = '.'.join((str(source),) + version)
79                action = SCons.Action.Action(symlink, "  Symlinking $TARGET ...")
80                last = env.Command(os.path.join(target_dir, target_name), last, action)
81                targets += last
82    return targets
83
84
85def createInstallMethods(env):
86    env.AddMethod(install_program, 'InstallProgram')
87    env.AddMethod(install_shared_library, 'InstallSharedLibrary')
88
89
90def num_jobs():
91    try:
92        return int(os.environ['NUMBER_OF_PROCESSORS'])
93    except (ValueError, KeyError):
94        pass
95
96    try:
97        return os.sysconf('SC_NPROCESSORS_ONLN')
98    except (ValueError, OSError, AttributeError):
99        pass
100
101    try:
102        return int(os.popen2("sysctl -n hw.ncpu")[1].read())
103    except ValueError:
104        pass
105
106    return 1
107
108
109def check_cc(env, cc, expr, cpp_opt = '-E'):
110    # Invoke C-preprocessor to determine whether the specified expression is
111    # true or not.
112
113    sys.stdout.write('Checking for %s ... ' % cc)
114
115    source = tempfile.NamedTemporaryFile(suffix='.c', delete=False)
116    source.write('#if !(%s)\n#error\n#endif\n' % expr)
117    source.close()
118
119    pipe = SCons.Action._subproc(env, [env['CC'], cpp_opt, source.name],
120                                 stdin = 'devnull',
121                                 stderr = 'devnull',
122                                 stdout = 'devnull')
123    result = pipe.wait() == 0
124
125    os.unlink(source.name)
126
127    sys.stdout.write(' %s\n' % ['no', 'yes'][int(bool(result))])
128    return result
129
130
131def generate(env):
132    """Common environment generation code"""
133
134    # Tell tools which machine to compile for
135    env['TARGET_ARCH'] = env['machine']
136    env['MSVS_ARCH'] = env['machine']
137
138    # Toolchain
139    platform = env['platform']
140    env.Tool(env['toolchain'])
141
142    # Allow override compiler and specify additional flags from environment
143    if os.environ.has_key('CC'):
144        env['CC'] = os.environ['CC']
145        # Update CCVERSION to match
146        pipe = SCons.Action._subproc(env, [env['CC'], '--version'],
147                                     stdin = 'devnull',
148                                     stderr = 'devnull',
149                                     stdout = subprocess.PIPE)
150        if pipe.wait() == 0:
151            line = pipe.stdout.readline()
152            match = re.search(r'[0-9]+(\.[0-9]+)+', line)
153            if match:
154                env['CCVERSION'] = match.group(0)
155    if os.environ.has_key('CFLAGS'):
156        env['CCFLAGS'] += SCons.Util.CLVar(os.environ['CFLAGS'])
157    if os.environ.has_key('CXX'):
158        env['CXX'] = os.environ['CXX']
159    if os.environ.has_key('CXXFLAGS'):
160        env['CXXFLAGS'] += SCons.Util.CLVar(os.environ['CXXFLAGS'])
161    if os.environ.has_key('LDFLAGS'):
162        env['LINKFLAGS'] += SCons.Util.CLVar(os.environ['LDFLAGS'])
163
164    # Detect gcc/clang not by executable name, but through pre-defined macros
165    # as autoconf does, to avoid drawing wrong conclusions when using tools
166    # that overrice CC/CXX like scan-build.
167    env['gcc'] = 0
168    env['clang'] = 0
169    env['msvc'] = 0
170    if _platform.system() == 'Windows':
171        env['msvc'] = check_cc(env, 'MSVC', 'defined(_MSC_VER)', '/E')
172    if not env['msvc']:
173        env['gcc'] = check_cc(env, 'GCC', 'defined(__GNUC__) && !defined(__clang__)')
174        env['clang'] = check_cc(env, 'Clang', '__clang__')
175    env['suncc'] = env['platform'] == 'sunos' and os.path.basename(env['CC']) == 'cc'
176    env['icc'] = 'icc' == os.path.basename(env['CC'])
177
178    if env['msvc'] and env['toolchain'] == 'default' and env['machine'] == 'x86_64':
179        # MSVC x64 support is broken in earlier versions of scons
180        env.EnsurePythonVersion(2, 0)
181
182    # shortcuts
183    machine = env['machine']
184    platform = env['platform']
185    x86 = env['machine'] == 'x86'
186    ppc = env['machine'] == 'ppc'
187    gcc_compat = env['gcc'] or env['clang']
188    msvc = env['msvc']
189    suncc = env['suncc']
190    icc = env['icc']
191
192    # Determine whether we are cross compiling; in particular, whether we need
193    # to compile code generators with a different compiler as the target code.
194    host_platform = _platform.system().lower()
195    if host_platform.startswith('cygwin'):
196        host_platform = 'cygwin'
197    host_machine = os.environ.get('PROCESSOR_ARCHITEW6432', os.environ.get('PROCESSOR_ARCHITECTURE', _platform.machine()))
198    host_machine = {
199        'x86': 'x86',
200        'i386': 'x86',
201        'i486': 'x86',
202        'i586': 'x86',
203        'i686': 'x86',
204        'ppc' : 'ppc',
205        'AMD64': 'x86_64',
206        'x86_64': 'x86_64',
207    }.get(host_machine, 'generic')
208    env['crosscompile'] = platform != host_platform
209    if machine == 'x86_64' and host_machine != 'x86_64':
210        env['crosscompile'] = True
211    env['hostonly'] = False
212
213    # Backwards compatability with the debug= profile= options
214    if env['build'] == 'debug':
215        if not env['debug']:
216            print 'scons: warning: debug option is deprecated and will be removed eventually; use instead'
217            print
218            print ' scons build=release'
219            print
220            env['build'] = 'release'
221        if env['profile']:
222            print 'scons: warning: profile option is deprecated and will be removed eventually; use instead'
223            print
224            print ' scons build=profile'
225            print
226            env['build'] = 'profile'
227    if False:
228        # Enforce SConscripts to use the new build variable
229        env.popitem('debug')
230        env.popitem('profile')
231    else:
232        # Backwards portability with older sconscripts
233        if env['build'] in ('debug', 'checked'):
234            env['debug'] = True
235            env['profile'] = False
236        if env['build'] == 'profile':
237            env['debug'] = False
238            env['profile'] = True
239        if env['build'] == 'release':
240            env['debug'] = False
241            env['profile'] = False
242
243    # Put build output in a separate dir, which depends on the current
244    # configuration. See also http://www.scons.org/wiki/AdvancedBuildExample
245    build_topdir = 'build'
246    build_subdir = env['platform']
247    if env['embedded']:
248        build_subdir =  'embedded-' + build_subdir
249    if env['machine'] != 'generic':
250        build_subdir += '-' + env['machine']
251    if env['build'] != 'release':
252        build_subdir += '-' +  env['build']
253    build_dir = os.path.join(build_topdir, build_subdir)
254    # Place the .sconsign file in the build dir too, to avoid issues with
255    # different scons versions building the same source file
256    env['build_dir'] = build_dir
257    env.SConsignFile(os.path.join(build_dir, '.sconsign'))
258    if 'SCONS_CACHE_DIR' in os.environ:
259        print 'scons: Using build cache in %s.' % (os.environ['SCONS_CACHE_DIR'],)
260        env.CacheDir(os.environ['SCONS_CACHE_DIR'])
261    env['CONFIGUREDIR'] = os.path.join(build_dir, 'conf')
262    env['CONFIGURELOG'] = os.path.join(os.path.abspath(build_dir), 'config.log')
263
264    # Parallel build
265    if env.GetOption('num_jobs') <= 1:
266        env.SetOption('num_jobs', num_jobs())
267
268    env.Decider('MD5-timestamp')
269    env.SetOption('max_drift', 60)
270
271    # C preprocessor options
272    cppdefines = []
273    if env['build'] in ('debug', 'checked'):
274        cppdefines += ['DEBUG']
275    else:
276        cppdefines += ['NDEBUG']
277    if env['build'] == 'profile':
278        cppdefines += ['PROFILE']
279    if env['platform'] in ('posix', 'linux', 'freebsd', 'darwin'):
280        cppdefines += [
281            '_POSIX_SOURCE',
282            ('_POSIX_C_SOURCE', '199309L'),
283            '_SVID_SOURCE',
284            '_BSD_SOURCE',
285            '_GNU_SOURCE',
286            'HAVE_PTHREAD',
287            'HAVE_POSIX_MEMALIGN',
288        ]
289        if env['platform'] == 'darwin':
290            cppdefines += [
291                '_DARWIN_C_SOURCE',
292                'GLX_USE_APPLEGL',
293                'GLX_DIRECT_RENDERING',
294            ]
295        else:
296            cppdefines += [
297                'GLX_DIRECT_RENDERING',
298                'GLX_INDIRECT_RENDERING',
299            ]
300        if env['platform'] in ('linux', 'freebsd'):
301            cppdefines += ['HAVE_ALIAS']
302        else:
303            cppdefines += ['GLX_ALIAS_UNSUPPORTED']
304    if env['platform'] == 'haiku':
305        cppdefines += [
306            'HAVE_PTHREAD',
307            'HAVE_POSIX_MEMALIGN'
308        ]
309    if platform == 'windows':
310        cppdefines += [
311            'WIN32',
312            '_WINDOWS',
313            #'_UNICODE',
314            #'UNICODE',
315            # http://msdn.microsoft.com/en-us/library/aa383745.aspx
316            ('_WIN32_WINNT', '0x0601'),
317            ('WINVER', '0x0601'),
318        ]
319        if gcc_compat:
320            cppdefines += [('__MSVCRT_VERSION__', '0x0700')]
321        if msvc:
322            cppdefines += [
323                'VC_EXTRALEAN',
324                '_USE_MATH_DEFINES',
325                '_CRT_SECURE_NO_WARNINGS',
326                '_CRT_SECURE_NO_DEPRECATE',
327                '_SCL_SECURE_NO_WARNINGS',
328                '_SCL_SECURE_NO_DEPRECATE',
329                '_ALLOW_KEYWORD_MACROS',
330            ]
331        if env['build'] in ('debug', 'checked'):
332            cppdefines += ['_DEBUG']
333    if platform == 'windows':
334        cppdefines += ['PIPE_SUBSYSTEM_WINDOWS_USER']
335    if env['embedded']:
336        cppdefines += ['PIPE_SUBSYSTEM_EMBEDDED']
337    if env['texture_float']:
338        print 'warning: Floating-point textures enabled.'
339        print 'warning: Please consult docs/patents.txt with your lawyer before building Mesa.'
340        cppdefines += ['TEXTURE_FLOAT_ENABLED']
341    env.Append(CPPDEFINES = cppdefines)
342
343    # C compiler options
344    cflags = [] # C
345    cxxflags = [] # C++
346    ccflags = [] # C & C++
347    if gcc_compat:
348        ccversion = env['CCVERSION']
349        if env['build'] == 'debug':
350            ccflags += ['-O0']
351        elif env['gcc'] and ccversion.startswith('4.2.'):
352            # gcc 4.2.x optimizer is broken
353            print "warning: gcc 4.2.x optimizer is broken -- disabling optimizations"
354            ccflags += ['-O0']
355        else:
356            ccflags += ['-O3']
357        if env['gcc']:
358            # gcc's builtin memcmp is slower than glibc's
359            # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43052
360            ccflags += ['-fno-builtin-memcmp']
361        # Work around aliasing bugs - developers should comment this out
362        ccflags += ['-fno-strict-aliasing']
363        ccflags += ['-g']
364        if env['build'] in ('checked', 'profile'):
365            # See http://code.google.com/p/jrfonseca/wiki/Gprof2Dot#Which_options_should_I_pass_to_gcc_when_compiling_for_profiling?
366            ccflags += [
367                '-fno-omit-frame-pointer',
368            ]
369            if env['gcc']:
370                ccflags += ['-fno-optimize-sibling-calls']
371        if env['machine'] == 'x86':
372            ccflags += [
373                '-m32',
374                #'-march=pentium4',
375            ]
376            if distutils.version.LooseVersion(ccversion) >= distutils.version.LooseVersion('4.2') \
377               and (platform != 'windows' or env['build'] == 'debug' or True) \
378               and platform != 'haiku':
379                # NOTE: We need to ensure stack is realigned given that we
380                # produce shared objects, and have no control over the stack
381                # alignment policy of the application. Therefore we need
382                # -mstackrealign ore -mincoming-stack-boundary=2.
383                #
384                # XXX: -O and -mstackrealign causes stack corruption on MinGW
385                #
386                # XXX: We could have SSE without -mstackrealign if we always used
387                # __attribute__((force_align_arg_pointer)), but that's not
388                # always the case.
389                ccflags += [
390                    '-mstackrealign', # ensure stack is aligned
391                    '-mmmx', '-msse', '-msse2', # enable SIMD intrinsics
392                    #'-mfpmath=sse',
393                ]
394            if platform in ['windows', 'darwin']:
395                # Workaround http://gcc.gnu.org/bugzilla/show_bug.cgi?id=37216
396                ccflags += ['-fno-common']
397            if platform in ['haiku']:
398                # Make optimizations compatible with Pentium or higher on Haiku
399                ccflags += [
400                    '-mstackrealign', # ensure stack is aligned
401                    '-march=i586', # Haiku target is Pentium
402                    '-mtune=i686' # use i686 where we can
403                ]
404        if env['machine'] == 'x86_64':
405            ccflags += ['-m64']
406            if platform == 'darwin':
407                ccflags += ['-fno-common']
408        if env['platform'] not in ('cygwin', 'haiku', 'windows'):
409            ccflags += ['-fvisibility=hidden']
410        # See also:
411        # - http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
412        ccflags += [
413            '-Wall',
414            '-Wno-long-long',
415            '-fmessage-length=0', # be nice to Eclipse
416        ]
417        cflags += [
418            '-Wmissing-prototypes',
419            '-std=gnu99',
420        ]
421        if distutils.version.LooseVersion(ccversion) >= distutils.version.LooseVersion('4.2'):
422            ccflags += [
423                '-Wpointer-arith',
424            ]
425            cflags += [
426                '-Wdeclaration-after-statement',
427            ]
428    if icc:
429        cflags += [
430            '-std=gnu99',
431        ]
432    if msvc:
433        # See also:
434        # - http://msdn.microsoft.com/en-us/library/19z1t1wy.aspx
435        # - cl /?
436        if 'MSVC_VERSION' not in env or distutils.version.LooseVersion(env['MSVC_VERSION']) < distutils.version.LooseVersion('12.0'):
437            # Use bundled stdbool.h and stdint.h headers for older MSVC
438            # versions.  stdint.h was introduced in MSVC 2010, but stdbool.h
439            # was only introduced in MSVC 2013.
440            top_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
441            env.Append(CPPPATH = [os.path.join(top_dir, 'include/c99')])
442        if env['build'] == 'debug':
443            ccflags += [
444              '/Od', # disable optimizations
445              '/Oi', # enable intrinsic functions
446            ]
447        else:
448            if 'MSVC_VERSION' in env and distutils.version.LooseVersion(env['MSVC_VERSION']) < distutils.version.LooseVersion('11.0'):
449                print 'scons: warning: Visual Studio versions prior to 2012 are known to produce incorrect code when optimizations are enabled ( https://bugs.freedesktop.org/show_bug.cgi?id=58718 )'
450            ccflags += [
451                '/O2', # optimize for speed
452            ]
453        if env['build'] == 'release':
454            ccflags += [
455                '/GL', # enable whole program optimization
456            ]
457        else:
458            ccflags += [
459                '/Oy-', # disable frame pointer omission
460                '/GL-', # disable whole program optimization
461            ]
462        ccflags += [
463            '/W3', # warning level
464            '/wd4244', # conversion from 'type1' to 'type2', possible loss of data
465            '/wd4305', # truncation from 'type1' to 'type2'
466            '/wd4800', # forcing value to bool 'true' or 'false' (performance warning)
467            '/wd4996', # disable deprecated POSIX name warnings
468        ]
469        if env['machine'] == 'x86':
470            ccflags += [
471                #'/arch:SSE2', # use the SSE2 instructions
472            ]
473        if platform == 'windows':
474            ccflags += [
475                # TODO
476            ]
477        # Automatic pdb generation
478        # See http://scons.tigris.org/issues/show_bug.cgi?id=1656
479        env.EnsureSConsVersion(0, 98, 0)
480        env['PDB'] = '${TARGET.base}.pdb'
481    env.Append(CCFLAGS = ccflags)
482    env.Append(CFLAGS = cflags)
483    env.Append(CXXFLAGS = cxxflags)
484
485    if env['platform'] == 'windows' and msvc:
486        # Choose the appropriate MSVC CRT
487        # http://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx
488        if env['build'] in ('debug', 'checked'):
489            env.Append(CCFLAGS = ['/MTd'])
490            env.Append(SHCCFLAGS = ['/LDd'])
491        else:
492            env.Append(CCFLAGS = ['/MT'])
493            env.Append(SHCCFLAGS = ['/LD'])
494
495    # Static code analysis
496    if env['analyze']:
497        if env['msvc']:
498            # http://msdn.microsoft.com/en-us/library/ms173498.aspx
499            env.Append(CCFLAGS = [
500                '/analyze',
501                #'/analyze:log', '${TARGET.base}.xml',
502            ])
503        if env['clang']:
504            # scan-build will produce more comprehensive output
505            env.Append(CCFLAGS = ['--analyze'])
506
507    # Assembler options
508    if gcc_compat:
509        if env['machine'] == 'x86':
510            env.Append(ASFLAGS = ['-m32'])
511        if env['machine'] == 'x86_64':
512            env.Append(ASFLAGS = ['-m64'])
513
514    # Linker options
515    linkflags = []
516    shlinkflags = []
517    if gcc_compat:
518        if env['machine'] == 'x86':
519            linkflags += ['-m32']
520        if env['machine'] == 'x86_64':
521            linkflags += ['-m64']
522        if env['platform'] not in ('darwin'):
523            shlinkflags += [
524                '-Wl,-Bsymbolic',
525            ]
526        # Handle circular dependencies in the libraries
527        if env['platform'] in ('darwin'):
528            pass
529        else:
530            env['_LIBFLAGS'] = '-Wl,--start-group ' + env['_LIBFLAGS'] + ' -Wl,--end-group'
531        if env['platform'] == 'windows':
532            # Avoid depending on gcc runtime DLLs
533            linkflags += ['-static-libgcc']
534            if 'w64' in env['CC'].split('-'):
535                linkflags += ['-static-libstdc++']
536            # Handle the @xx symbol munging of DLL exports
537            shlinkflags += ['-Wl,--enable-stdcall-fixup']
538            #shlinkflags += ['-Wl,--kill-at']
539    if msvc:
540        if env['build'] == 'release':
541            # enable Link-time Code Generation
542            linkflags += ['/LTCG']
543            env.Append(ARFLAGS = ['/LTCG'])
544    if platform == 'windows' and msvc:
545        # See also:
546        # - http://msdn2.microsoft.com/en-us/library/y0zzbyt4.aspx
547        linkflags += [
548            '/fixed:no',
549            '/incremental:no',
550        ]
551    env.Append(LINKFLAGS = linkflags)
552    env.Append(SHLINKFLAGS = shlinkflags)
553
554    # We have C++ in several libraries, so always link with the C++ compiler
555    if gcc_compat:
556        env['LINK'] = env['CXX']
557
558    # Default libs
559    libs = []
560    if env['platform'] in ('darwin', 'freebsd', 'linux', 'posix', 'sunos'):
561        libs += ['m', 'pthread', 'dl']
562    if env['platform'] in ('linux',):
563        libs += ['rt']
564    if env['platform'] in ('haiku'):
565        libs += ['root', 'be', 'network', 'translation']
566    env.Append(LIBS = libs)
567
568    # OpenMP
569    if env['openmp']:
570        if env['msvc']:
571            env.Append(CCFLAGS = ['/openmp'])
572            # When building openmp release VS2008 link.exe crashes with LNK1103 error.
573            # Workaround: overwrite PDB flags with empty value as it isn't required anyways
574            if env['build'] == 'release':
575                env['PDB'] = ''
576        if env['gcc']:
577            env.Append(CCFLAGS = ['-fopenmp'])
578            env.Append(LIBS = ['gomp'])
579
580    # Load tools
581    env.Tool('lex')
582    env.Tool('yacc')
583    if env['llvm']:
584        env.Tool('llvm')
585
586    # Custom builders and methods
587    env.Tool('custom')
588    createInstallMethods(env)
589
590    env.PkgCheckModules('X11', ['x11', 'xext', 'xdamage', 'xfixes'])
591    env.PkgCheckModules('XCB', ['x11-xcb', 'xcb-glx >= 1.8.1', 'xcb-dri2 >= 1.8'])
592    env.PkgCheckModules('XF86VIDMODE', ['xxf86vm'])
593    env.PkgCheckModules('DRM', ['libdrm >= 2.4.38'])
594    env.PkgCheckModules('UDEV', ['libudev >= 151'])
595
596    env['dri'] = env['x11'] and env['drm']
597
598    # for debugging
599    #print env.Dump()
600
601
602def exists(env):
603    return 1
604