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

Last change on this file since 259 was 199, checked in by tim, 15 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.