source: trunk/python/experimental/utils.py @ 260

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

reworked part of regfi C API to make python wrappers simpler
continued work on python wrappers
fixed some issues in pyregfi-smoketest. WIP

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