source: trunk/python2/utils.py @ 196

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

experimental python bindings generator as provided by Michael Cohen

File size: 17.6 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(
292                "-I"+sysconfig.get_python_inc())
293            shlink_flags = str(self['LINKFLAGS']).split()
294
295        install_dest = distutils.util.split_quoted(
296            os.path.join(
297                sysconfig.get_config_var('BINLIBDEST'),os.path.dirname(libname)))
298
299        flags = distutils.util.split_quoted(
300            sysconfig.get_config_var('LDSHARED'))
301
302        ## For some stupid reason they include the compiler in LDSHARED
303        shlink_flags.extend([x for x in flags if 'gcc' not in x])
304
305        shlink_flags.append(sysconfig.get_config_var('LOCALMODLIBS'))
306
307        ## TODO cross compile mode
308        kwargs['LIBPREFIX'] = ''
309        kwargs['CPPFLAGS'] = cppflags
310        kwargs['SHLIBSUFFIX'] = shlib_suffix
311        kwargs['SHLINKFLAGS'] = shlink_flags
312
313        if not self.config.V:
314            kwargs['SHCCCOMSTR'] = compile_python_source_message
315            kwargs['SHLINKCOMSTR'] = link_python_module_message
316
317        lib = self.SharedLibrary(libname,lib_objs,
318                                 **kwargs)
319
320        ## Install it to the right spot
321        self.Install(install_dest, lib)
322        self.Alias('install', install_dest)
323
324        return lib
325
326    def VersionedSharedLibrary(self, libname, libversion, lib_objs=[]):
327        """ This creates a version library similar to libtool.
328
329        We name the library with the appropriate soname.
330        """
331        platform = self.subst('$PLATFORM')
332        shlib_pre_action = None
333        shlib_suffix = self.subst('$SHLIBSUFFIX')
334        shlib_post_action = None
335        shlink_flags = SCons.Util.CLVar(self.subst('$SHLINKFLAGS'))
336
337        if platform == 'posix':
338            shlib_post_action = [ 'rm -f $TARGET', 'ln -s ${SOURCE.file} $TARGET' ]
339            shlib_post_action_output_re = [ '%s\\.[0-9\\.]*$' % re.escape(shlib_suffix), shlib_suffix ]
340            shlib_suffix += '.' + libversion
341            shlink_flags += [ '-Wl,-Bsymbolic', '-Wl,-soname=${LIBPREFIX}%s%s' % (
342                    libname, shlib_suffix) ]
343        elif platform == 'aix':
344            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]" ]
345            shlib_pre_action_output_re = [ '$', '.exp' ]
346            shlib_post_action = [ 'rm -f $TARGET', 'ln -s $SOURCE $TARGET' ]
347            shlib_post_action_output_re = [ '%s\\.[0-9\\.]*' % re.escape(shlib_suffix), shlib_suffix ]
348            shlib_suffix += '.' + libversion
349            shlink_flags += ['-G', '-bE:${TARGET}.exp', '-bM:SRE']
350        elif platform == 'cygwin':
351            shlink_flags += [ '-Wl,-Bsymbolic', '-Wl,--out-implib,${TARGET.base}.a' ]
352        elif platform == 'darwin':
353            shlib_suffix = '.' + libversion + shlib_suffix
354            shlink_flags += [ '-dynamiclib', '-current-version %s' % libversion ]
355
356        lib = self.SharedLibrary(libname,lib_objs,
357                                 SHLIBSUFFIX=shlib_suffix,
358                                 SHLINKFLAGS=shlink_flags)
359
360        if shlib_pre_action:
361            shlib_pre_action_output = re.sub(shlib_pre_action_output_re[0], shlib_pre_action_output_re[1], str(lib[0]))
362            self.Command(shlib_pre_action_output, [ lib_objs ], shlib_pre_action)
363            self.Depends(lib, shlib_pre_action_output)
364
365        if shlib_post_action:
366            shlib_post_action_output = re.sub(shlib_post_action_output_re[0], shlib_post_action_output_re[1], str(lib[0]))
367            self.Command(shlib_post_action_output, lib, shlib_post_action)
368
369        return lib
370
371    def InstallVersionedSharedLibrary(self, destination, lib):
372        platform = self.subst('$PLATFORM')
373        shlib_suffix = self.subst('$SHLIBSUFFIX')
374        shlib_install_pre_action = None
375        shlib_install_post_action = None
376
377        if platform == 'posix':
378            shlib_post_action = [ 'rm -f $TARGET', 'ln -s ${SOURCE.file} $TARGET' ]
379            shlib_post_action_output_re = [ '%s\\.[0-9\\.]*$' % re.escape(shlib_suffix), shlib_suffix ]
380            shlib_install_post_action = shlib_post_action
381            shlib_install_post_action_output_re = shlib_post_action_output_re
382
383        ilib = self.Install(destination,lib)
384
385        if shlib_install_pre_action:
386            shlib_install_pre_action_output = re.sub(shlib_install_pre_action_output_re[0], shlib_install_pre_action_output_re[1], str(ilib[0]))
387            self.Command(shlib_install_pre_action_output, ilib, shlib_install_pre_action)
388            self.Depends(shlib_install_pre_action_output, ilib)
389
390        if shlib_install_post_action:
391            shlib_install_post_action_output = re.sub(shlib_install_post_action_output_re[0], shlib_install_post_action_output_re[1], str(ilib[0]))
392            self.Command(shlib_install_post_action_output, ilib, shlib_install_post_action)
393
394
395import subprocess
396
397def pkg_config(pkg, type):
398    try:
399        result = subprocess.Popen(["%s-config" % pkg, "--%s" % type],
400                                  stdout=subprocess.PIPE).communicate()[0]
401    except:
402        error("Unable to run %s-config - do you have the dev package installed?" % pkg)
403
404    return result.strip()
405
406
407# Sensible default for common types on common platforms.
408_DEFAULTS = {
409    'char': [1,],
410    'short' : [2,],
411    'int' : [4, 2],
412    'long' : [4, 8],
413    'long long' : [8, 4],
414    # Normally, there is no need to check unsigned types, because they are
415    # guaranteed to be of the same size than their signed counterpart.
416    'unsigned char': [1,],
417    'unsigned short' : [2,],
418    'unsigned int' : [4, 2],
419    'unsigned long' : [4, 8],
420    'unsigned long long' : [8, 4],
421    'float' : [4,],
422    'double' : [8,],
423    'long double' : [12,],
424    'size_t' : [4,],
425}
426
427def CheckTypeSize(context, type, includes = None, language = 'C', size = None):
428    """This check can be used to get the size of a given type, or to check whether
429    the type is of expected size.
430
431    Arguments:
432        - type : str
433            the type to check
434        - includes : sequence
435            list of headers to include in the test code before testing the type
436        - language : str
437            'C' or 'C++'
438        - size : int
439            if given, will test wether the type has the given number of bytes.
440            If not given, will test against a list of sizes (all sizes between
441            0 and 16 bytes are tested).
442
443        Returns:
444                status : int
445                        0 if the check failed, or the found size of the type if the check succeeded."""
446    minsz = 0
447    maxsz = 16
448
449    if includes:
450        src = "\n".join([r"#include <%s>\n" % i for i in includes])
451    else:
452        src = ""
453
454    if language == 'C':
455        ext = '.c'
456    elif language == 'C++':
457        ext = '.cpp'
458    else:
459        raise NotImplementedError("%s is not a recognized language" % language)
460
461    # test code taken from autoconf: this is a pretty clever hack to find that
462    # a type is of a given size using only compilation. This speeds things up
463    # quite a bit compared to straightforward code using TryRun
464    src += r"""
465typedef %s scons_check_type;
466
467int main()
468{
469    static int test_array[1 - 2 * !(((long int) (sizeof(scons_check_type))) <= %d)];
470    test_array[0] = 0;
471
472    return 0;
473}
474"""
475
476    if size:
477        # Only check if the given size is the right one
478        context.Message('Checking %s is %d bytes... ' % (type, size))
479        st = context.TryCompile(src % (type, size), ext)
480        context.Result(st)
481
482        if st:
483            return size
484        else:
485            return 0
486    else:
487        # Only check if the given size is the right one
488        context.Message('Checking size of %s ... ' % type)
489
490        # Try sensible defaults first
491        try:
492            szrange = _DEFAULTS[type]
493        except KeyError:
494            szrange = []
495        szrange.extend(xrange(minsz, maxsz))
496        st = 0
497
498        # Actual test
499        for sz in szrange:
500            st = context.TryCompile(src % (type, sz), ext)
501            if st:
502                break
503
504        if st:
505            context.Result('%d' % sz)
506            return sz
507        else:
508            context.Result('Failed !')
509            return 0
510
511#For example, to check wether long is 4 bytes on your platform, you can do:
512#config.CheckTypeSize('long', size = 4).
513## Now check the sizes
Note: See TracBrowser for help on using the repository browser.