source: trunk/python2/utils.py @ 198

Last change on this file since 198 was 198, checked in by tim, 14 years ago

misc tweaks to run under python2.5-dbg

File size: 17.5 KB
Line 
1import os, sys, re, pdb
2import distutils.sysconfig as sysconfig
3import distutils.util
4import platform
5import SCons.SConf as SConf
6import config
7
8# taken from scons wiki
9def CheckPKGConfig(context, version):
10    context.Message( 'Checking for pkg-config version > %s... ' % version)
11    ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
12    context.Result( ret )
13    return ret
14
15def CheckFramework(context, name):
16    ret = 0
17    if (platform.system().lower() == 'darwin'):
18        context.Message( '\nLooking for framework %s... ' % name )
19        lastFRAMEWORKS = context.env['FRAMEWORKS']
20        context.env.Append(FRAMEWORKS = [name])
21        ret = context.TryLink("""
22              int main(int argc, char **argv) {
23                return 0;
24              }
25              """, '.c')
26        if not ret:
27            context.env.Replace(FRAMEWORKS = lastFRAMEWORKS
28)
29
30    return ret
31
32def CheckFink(context):
33    context.Message( 'Looking for fink... ')
34    prog = context.env.WhereIs('fink')
35    if prog:
36        ret = 1
37        prefix = prog.rsplit(os.sep, 2)[0]
38        context.env.Append(LIBPATH = [prefix + os.sep +'lib'],
39                           CPPPATH = [prefix + os.sep +'include'])
40        context.Message( 'Adding fink lib and include path')
41    else:
42        ret = 0
43       
44    context.Result(ret)   
45    return int(ret)
46
47def CheckMacports(context):
48    context.Message( 'Looking for macports... ')
49    prog = context.env.WhereIs('port')
50    if prog:
51        ret = 1
52        prefix = prog.rsplit(os.sep, 2)[0]
53        context.env.Append(LIBPATH = [prefix + os.sep + 'lib'],
54                           CPPPATH = [prefix + os.sep + 'include'])
55        context.Message( 'Adding port lib and include path')
56    else:
57        ret = 0
58       
59    context.Result(ret)   
60    return int(ret)
61
62# TODO: We should use the scons one instead
63def CheckLib(context, name):
64    context.Message( 'Looking for lib %s... ' % name )
65    lastLIBS = context.env['LIBS']
66    context.env.Append(LIBS = [name])
67    ret = context.TryLink("""
68              int main(int argc, char **argv) {
69                return 0;
70              }
71              """,'.c')
72    if not ret:
73        context.env.Replace(LIBS = lastLIBS)
74
75    return ret
76
77def ConfigPKG(context, name):
78    context.Message( '\nUsing pkg-config for %s... ' % name )
79    ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0]
80    context.Result(  ret )
81    if ret:
82        context.env.ParseConfig('pkg-config --cflags --libs \'%s\'' % name)
83    return int(ret)
84
85def CheckPKG(context, name):
86    context.Message( 'Checking for %s... ' % name )
87    if platform.system().lower() == 'windows':
88        return 0 
89    ret = 1
90    if not CheckFramework(context, name):
91        if not ConfigPKG(context, name.lower()):
92            ret = CheckLib(context, name) 
93
94    context.Result(ret)
95    return int(ret)
96
97
98## Configure colors for pretty builds
99colors = {}
100colors['cyan']   = '\033[96m'
101colors['purple'] = '\033[95m'
102colors['blue']   = '\033[94m'
103colors['green']  = '\033[92m'
104colors['yellow'] = '\033[93m'
105colors['red']    = '\033[91m'
106colors['end']    = '\033[0m'
107
108#If the output is not a terminal, remove the colors
109if not sys.stdout.isatty():
110   for key, value in colors.iteritems():
111      colors[key] = ''
112
113def error(msg):
114   print "%s%s%s" % (colors['red'], msg, colors['end'])
115   sys.exit(1)
116
117def warn(msg):
118   print "%s%s%s" % (colors['yellow'], msg, colors['end'])
119
120compile_source_message = '%sCompiling %s==> %s$SOURCE%s' % \
121   (colors['blue'], colors['purple'], colors['yellow'], colors['end'])
122
123compile_shared_source_message = '%sCompiling shared %s==> %s$SOURCE%s' % \
124   (colors['blue'], colors['purple'], colors['yellow'], colors['end'])
125
126compile_python_source_message = '%sCompiling python module %s==> %s$SOURCE%s' % \
127   (colors['blue'], colors['purple'], colors['yellow'], colors['end'])
128
129link_program_message = '%sLinking Program %s==> %s$TARGET%s' % \
130   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
131
132link_library_message = '%sLinking Static Library %s==> %s$TARGET%s' % \
133   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
134
135ranlib_library_message = '%sRanlib Library %s==> %s$TARGET%s' % \
136   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
137
138link_shared_library_message = '%sLinking Shared Library %s==> %s$TARGET%s' % \
139   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
140
141link_python_module_message = '%sLinking Native Python module %s==> %s${TARGET}%s' % \
142   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
143
144java_library_message = '%sCreating Java Archive %s==> %s$TARGET%s' % \
145   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
146
147def install_colors(args):
148    """ Installs colors into an environment """
149    args.update(dict( CXXCOMSTR = compile_source_message,
150                      CCCOMSTR = compile_source_message,
151                      SHCCCOMSTR = compile_shared_source_message,
152                      SHCXXCOMSTR = compile_shared_source_message,
153                      ARCOMSTR = link_library_message,
154                      RANLIBCOMSTR = ranlib_library_message,
155                      SHLINKCOMSTR = link_shared_library_message,
156                      LINKCOMSTR = link_program_message,
157                      JARCOMSTR = java_library_message,
158                      JAVACCOMSTR = compile_source_message,))
159
160import optparse
161
162### This workaround is because scons does not provide access to the
163### parser, and by setting Help() we are unable to generate the option
164### listing from AddOption
165my_parser = optparse.OptionParser()
166
167import SCons.Script.Main as Main
168import SCons.Script as Script
169
170def add_option(arg, option, *args, **kwargs):
171    opt = "--%s" % option
172    Main.AddOption(opt, *args, **kwargs)
173    my_parser.add_option(opt, *args, **kwargs)
174
175    arg[option] = Main.GetOption(option)
176
177def generate_help(vars, env):
178    Script.Help("AFF4 build system configuration.\n\nFollowing are compile time options:\n")
179    Script.Help(my_parser.format_help())
180    Script.Help("\nThe following variables can be used on the command line:\n")
181    Script.Help(vars.GenerateHelpText(env))
182
183HEADERS = {}
184
185def check_size(conf, types):
186    global _DEFAULTS
187
188    for t in types:
189        name = "SIZEOF_" + t.replace(" ","_").upper()
190        HEADERS[name] = conf.CheckTypeSize(
191            t, size = _DEFAULTS[t][0])
192
193def check_type(conf, types):
194    header = None
195    for t in types:
196        if ':' in t:
197            t, header = t.split(':')
198        define = "HAVE_" + t.upper().replace(".","_")
199
200        result = 0
201        if conf.CheckType(t, includes="#include <%s>\n" % header):
202            result = 1
203
204        HEADERS[define] = result
205
206def check_build(conf, message, define, prog):
207    """ Build and links prog and adds define if that succeeds """
208    context = SConf.CheckContext(conf)
209    context.Message("Checking for %s ..." % message)
210    if context.TryLink(prog, ".c"):
211        HEADERS[define] = 1
212        context.Message("yes\n")
213    else:
214        context.Message("no\n")
215
216def check(type, conf, headers, extra_include =''):
217    for header in headers:
218        if ":" in header:
219            define, header = header.split(':')
220        else:
221            if "/" in header:
222                tmp = header.split("/")[-1]
223            else:
224                tmp = header
225
226            define = "HAVE_" + tmp.upper().replace(".","_")
227
228        global HEADERS
229        result = 0
230        if type == 'header':
231            #pdb.set_trace()
232            if conf.CheckCHeader(header): result = 1
233            HEADERS[define] = result
234        elif type == 'func':
235            if conf.CheckFunc(header, header=extra_include): result = 1
236            HEADERS[define] = result
237        elif type == 'lib':
238            if conf.CheckLib(header): result =1
239            HEADERS[define] = result
240
241## Build the config.h file
242def config_h_build(target, source, env):
243    config_h_defines = env.Dictionary()
244    config_h_defines.update(env.config.__dict__)
245    warn("Generating %s" % (target[0].path))
246
247    for a_target, a_source in zip(target, source):
248        config_h = file(str(a_target), "w")
249        config_h_in = file(str(a_source), "r")
250        config_h.write(config_h_in.read() % config_h_defines)
251        config_h_in.close()
252
253    keys = HEADERS.keys()
254    keys.sort()
255
256    for k in keys:
257        if HEADERS[k]:
258            config_h.write("#define %s %s\n" % (k,HEADERS[k]))
259        else:
260            config_h.write("/** %s unset */\n" % k)
261
262    config_h.close()
263
264import SCons.Environment
265
266class ExtendedEnvironment(SCons.Environment.Environment):
267    """ Implementation from Richard Levitte email to
268    org.tigris.scons.dev dated Jan 26, 2006 7:05:10 am."""
269    python_cppflags = distutils.util.split_quoted(
270        "-I"+sysconfig.get_python_inc())
271
272    def PythonModule(self, libname, lib_objs=[], **kwargs):
273        """ This builds a python module which is almost a library but
274        is sometimes named differently.
275
276        We have two modes - a cross compile mode where we do our best
277        to guess the flags. In the native mode we can get the required
278        flags directly from distutils.
279        """
280        if config.MINGW_XCOMPILE:
281            shlib_suffix = ".pyd"
282            cppflags = "-I%s" % config.XCOMPILE_PYTHON_PATH
283            shlink_flags = ['']
284
285        else:
286            platform = self.subst('$PLATFORM')
287            shlib_pre_action = None
288            shlib_suffix = distutils.util.split_quoted(
289                sysconfig.get_config_var('SO'))
290            shlib_post_action = None
291            cppflags = distutils.util.split_quoted(self.python_cppflags)
292            shlink_flags = str(self['LINKFLAGS']).split()
293
294        install_dest = distutils.util.split_quoted(
295            os.path.join(
296                sysconfig.get_config_var('BINLIBDEST'),os.path.dirname(libname)))
297
298        flags = distutils.util.split_quoted(
299            sysconfig.get_config_var('LDSHARED'))
300
301        ## For some stupid reason they include the compiler in LDSHARED
302        shlink_flags.extend([x for x in flags if 'gcc' not in x])
303
304        shlink_flags.append(sysconfig.get_config_var('LOCALMODLIBS'))
305
306        ## TODO cross compile mode
307        kwargs['LIBPREFIX'] = ''
308        kwargs['CPPFLAGS'] = cppflags
309        kwargs['SHLIBSUFFIX'] = shlib_suffix
310        kwargs['SHLINKFLAGS'] = shlink_flags
311
312        if not self.config.V:
313            kwargs['SHCCCOMSTR'] = compile_python_source_message
314            kwargs['SHLINKCOMSTR'] = link_python_module_message
315
316        lib = self.SharedLibrary(libname,lib_objs,
317                                 **kwargs)
318
319        ## Install it to the right spot
320        self.Install(install_dest, lib)
321        self.Alias('install', install_dest)
322
323        return lib
324
325    def VersionedSharedLibrary(self, libname, libversion, lib_objs=[]):
326        """ This creates a version library similar to libtool.
327
328        We name the library with the appropriate soname.
329        """
330        platform = self.subst('$PLATFORM')
331        shlib_pre_action = None
332        shlib_suffix = self.subst('$SHLIBSUFFIX')
333        shlib_post_action = None
334        shlink_flags = SCons.Util.CLVar(self.subst('$SHLINKFLAGS'))
335
336        if platform == 'posix':
337            shlib_post_action = [ 'rm -f $TARGET', 'ln -s ${SOURCE.file} $TARGET' ]
338            shlib_post_action_output_re = [ '%s\\.[0-9\\.]*$' % re.escape(shlib_suffix), shlib_suffix ]
339            shlib_suffix += '.' + libversion
340            shlink_flags += [ '-Wl,-Bsymbolic', '-Wl,-soname=${LIBPREFIX}%s%s' % (
341                    libname, shlib_suffix) ]
342        elif platform == 'aix':
343            shlib_pre_action = [ "nm -Pg $SOURCES > ${TARGET}.tmp1", "grep ' [BDT] ' < ${TARGET}.tmp1 > ${TARGET}.tmp2", "cut -f1 -d' ' < ${TARGET}.tmp2 > ${TARGET}", "rm -f ${TARGET}.tmp[12]" ]
344            shlib_pre_action_output_re = [ '$', '.exp' ]
345            shlib_post_action = [ 'rm -f $TARGET', 'ln -s $SOURCE $TARGET' ]
346            shlib_post_action_output_re = [ '%s\\.[0-9\\.]*' % re.escape(shlib_suffix), shlib_suffix ]
347            shlib_suffix += '.' + libversion
348            shlink_flags += ['-G', '-bE:${TARGET}.exp', '-bM:SRE']
349        elif platform == 'cygwin':
350            shlink_flags += [ '-Wl,-Bsymbolic', '-Wl,--out-implib,${TARGET.base}.a' ]
351        elif platform == 'darwin':
352            shlib_suffix = '.' + libversion + shlib_suffix
353            shlink_flags += [ '-dynamiclib', '-current-version %s' % libversion ]
354
355        lib = self.SharedLibrary(libname,lib_objs,
356                                 SHLIBSUFFIX=shlib_suffix,
357                                 SHLINKFLAGS=shlink_flags)
358
359        if shlib_pre_action:
360            shlib_pre_action_output = re.sub(shlib_pre_action_output_re[0], shlib_pre_action_output_re[1], str(lib[0]))
361            self.Command(shlib_pre_action_output, [ lib_objs ], shlib_pre_action)
362            self.Depends(lib, shlib_pre_action_output)
363
364        if shlib_post_action:
365            shlib_post_action_output = re.sub(shlib_post_action_output_re[0], shlib_post_action_output_re[1], str(lib[0]))
366            self.Command(shlib_post_action_output, lib, shlib_post_action)
367
368        return lib
369
370    def InstallVersionedSharedLibrary(self, destination, lib):
371        platform = self.subst('$PLATFORM')
372        shlib_suffix = self.subst('$SHLIBSUFFIX')
373        shlib_install_pre_action = None
374        shlib_install_post_action = None
375
376        if platform == 'posix':
377            shlib_post_action = [ 'rm -f $TARGET', 'ln -s ${SOURCE.file} $TARGET' ]
378            shlib_post_action_output_re = [ '%s\\.[0-9\\.]*$' % re.escape(shlib_suffix), shlib_suffix ]
379            shlib_install_post_action = shlib_post_action
380            shlib_install_post_action_output_re = shlib_post_action_output_re
381
382        ilib = self.Install(destination,lib)
383
384        if shlib_install_pre_action:
385            shlib_install_pre_action_output = re.sub(shlib_install_pre_action_output_re[0], shlib_install_pre_action_output_re[1], str(ilib[0]))
386            self.Command(shlib_install_pre_action_output, ilib, shlib_install_pre_action)
387            self.Depends(shlib_install_pre_action_output, ilib)
388
389        if shlib_install_post_action:
390            shlib_install_post_action_output = re.sub(shlib_install_post_action_output_re[0], shlib_install_post_action_output_re[1], str(ilib[0]))
391            self.Command(shlib_install_post_action_output, ilib, shlib_install_post_action)
392
393
394import subprocess
395
396def pkg_config(pkg, type):
397    try:
398        result = subprocess.Popen(["%s-config" % pkg, "--%s" % type],
399                                  stdout=subprocess.PIPE).communicate()[0]
400    except:
401        error("Unable to run %s-config - do you have the dev package installed?" % pkg)
402
403    return result.strip()
404
405
406# Sensible default for common types on common platforms.
407_DEFAULTS = {
408    'char': [1,],
409    'short' : [2,],
410    'int' : [4, 2],
411    'long' : [4, 8],
412    'long long' : [8, 4],
413    # Normally, there is no need to check unsigned types, because they are
414    # guaranteed to be of the same size than their signed counterpart.
415    'unsigned char': [1,],
416    'unsigned short' : [2,],
417    'unsigned int' : [4, 2],
418    'unsigned long' : [4, 8],
419    'unsigned long long' : [8, 4],
420    'float' : [4,],
421    'double' : [8,],
422    'long double' : [12,],
423    'size_t' : [4,],
424}
425
426def CheckTypeSize(context, type, includes = None, language = 'C', size = None):
427    """This check can be used to get the size of a given type, or to check whether
428    the type is of expected size.
429
430    Arguments:
431        - type : str
432            the type to check
433        - includes : sequence
434            list of headers to include in the test code before testing the type
435        - language : str
436            'C' or 'C++'
437        - size : int
438            if given, will test wether the type has the given number of bytes.
439            If not given, will test against a list of sizes (all sizes between
440            0 and 16 bytes are tested).
441
442        Returns:
443                status : int
444                        0 if the check failed, or the found size of the type if the check succeeded."""
445    minsz = 0
446    maxsz = 16
447
448    if includes:
449        src = "\n".join([r"#include <%s>\n" % i for i in includes])
450    else:
451        src = ""
452
453    if language == 'C':
454        ext = '.c'
455    elif language == 'C++':
456        ext = '.cpp'
457    else:
458        raise NotImplementedError("%s is not a recognized language" % language)
459
460    # test code taken from autoconf: this is a pretty clever hack to find that
461    # a type is of a given size using only compilation. This speeds things up
462    # quite a bit compared to straightforward code using TryRun
463    src += r"""
464typedef %s scons_check_type;
465
466int main()
467{
468    static int test_array[1 - 2 * !(((long int) (sizeof(scons_check_type))) <= %d)];
469    test_array[0] = 0;
470
471    return 0;
472}
473"""
474
475    if size:
476        # Only check if the given size is the right one
477        context.Message('Checking %s is %d bytes... ' % (type, size))
478        st = context.TryCompile(src % (type, size), ext)
479        context.Result(st)
480
481        if st:
482            return size
483        else:
484            return 0
485    else:
486        # Only check if the given size is the right one
487        context.Message('Checking size of %s ... ' % type)
488
489        # Try sensible defaults first
490        try:
491            szrange = _DEFAULTS[type]
492        except KeyError:
493            szrange = []
494        szrange.extend(xrange(minsz, maxsz))
495        st = 0
496
497        # Actual test
498        for sz in szrange:
499            st = context.TryCompile(src % (type, sz), ext)
500            if st:
501                break
502
503        if st:
504            context.Result('%d' % sz)
505            return sz
506        else:
507            context.Result('Failed !')
508            return 0
509
510#For example, to check wether long is 4 bytes on your platform, you can do:
511#config.CheckTypeSize('long', size = 4).
512## Now check the sizes
Note: See TracBrowser for help on using the repository browser.